├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon-29.png │ │ │ ├── AppIcon@2x.png │ │ │ ├── AppIcon@3x.png │ │ │ ├── AppIcon-20@2x.png │ │ │ ├── AppIcon-20@3x.png │ │ │ ├── AppIcon-29@2x.png │ │ │ ├── AppIcon-29@3x.png │ │ │ ├── AppIcon-40@2x.png │ │ │ ├── AppIcon-40@3x.png │ │ │ ├── AppIcon~ipad.png │ │ │ ├── AppIcon-20~ipad.png │ │ │ ├── AppIcon-29~ipad.png │ │ │ ├── AppIcon-40~ipad.png │ │ │ ├── AppIcon@2x~ipad.png │ │ │ ├── AppIcon-20@2x~ipad.png │ │ │ ├── AppIcon-29@2x~ipad.png │ │ │ ├── AppIcon-40@2x~ipad.png │ │ │ ├── AppIcon-60@2x~car.png │ │ │ ├── AppIcon-60@3x~car.png │ │ │ ├── AppIcon-83.5@2x~ipad.png │ │ │ ├── AppIcon~ios-marketing.png │ │ │ └── Contents.json │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── LaunchBackground.imageset │ │ │ ├── background.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── GoogleService-Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── firebase_app_id_file.json └── .gitignore ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── drawable │ │ │ │ │ ├── background.png │ │ │ │ │ ├── notifications_logo.png │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-hdpi │ │ │ │ │ └── splash.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ └── splash.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ └── splash.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ └── splash.png │ │ │ │ ├── drawable-v21 │ │ │ │ │ ├── background.png │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ └── splash.png │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── values-v31 │ │ │ │ │ └── styles.xml │ │ │ │ ├── values-night-v31 │ │ │ │ │ └── styles.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── money_transfer_app │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── google-services.json │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── assets ├── images │ ├── logo.png │ ├── empty_list.png │ ├── main_logo.png │ ├── error_image.png │ ├── no-internet.png │ ├── card_chip_image.png │ ├── gradient_circle.png │ ├── Pay Mobile advert.png │ ├── notifications_logo.png │ ├── pay_mobile_advert.png │ ├── dialog_success_image.png │ ├── pin_feature_showcase.png │ ├── responsive_showcase.png │ ├── pay_mobile_full_stack.png │ ├── customer_support_showcase.png │ ├── forgort_password_showcase.png │ ├── in_app_notification_image.png │ ├── success_dialogs_showcase.png │ ├── push_notification_showcase.png │ ├── signup_verification_showcase.png │ ├── transaction_details_showcase.png │ ├── username_transfer_showcase.png │ └── username_search_success_showcase.png ├── icons │ ├── add_icon.png │ ├── card_icon.png │ ├── chat_icon.png │ ├── home_icon.png │ ├── more_icon.png │ ├── send_icon.png │ ├── wifi_icon.png │ ├── bills_icon.png │ ├── budget_icon.png │ ├── credit_icon.png │ ├── debit_icon.png │ ├── info-circle.png │ ├── logout_icon.png │ ├── mobile_icon.png │ ├── notification.png │ ├── profile_icon.png │ ├── settings_icon.png │ ├── shopping_icon.png │ ├── contactless_icon.png │ ├── electricity_icon.png │ └── transactions_icon.png └── fonts │ ├── Comfortaa-Bold.ttf │ ├── Comfortaa-Medium.ttf │ ├── Comfortaa-Regular.ttf │ └── Comfortaa-SemiBold.ttf ├── lib ├── core │ ├── utils │ │ ├── custom_exception_handler.dart │ │ ├── color_constants.dart │ │ ├── assets.dart │ │ ├── validators.dart │ │ ├── custom_notifications.dart │ │ └── global_constants.dart │ └── error │ │ └── error_handler.dart ├── features │ ├── profile │ │ ├── screens │ │ │ ├── security_screen.dart │ │ │ └── chat_screen.dart │ │ ├── providers │ │ │ └── chat_provider.dart │ │ ├── models │ │ │ ├── chat_model.dart │ │ │ └── message_model.dart │ │ └── widgets │ │ │ ├── receivers_message_card.dart.dart │ │ │ ├── senders_message_card.dart │ │ │ └── profile_card.dart │ ├── auth │ │ ├── providers │ │ │ ├── auth_provider.dart │ │ │ └── user_provider.dart │ │ ├── models │ │ │ └── user.dart │ │ └── screens │ │ │ ├── create_new_password_screen.dart │ │ │ ├── forgort_pin_screen.dart │ │ │ ├── forgort_password_screen.dart │ │ │ └── login_screen.dart │ ├── transactions │ │ ├── models │ │ │ ├── transfer.dart │ │ │ └── transactions.dart │ │ ├── services │ │ │ └── transactions_services.dart │ │ └── widgets │ │ │ ├── transaction_details_container.dart │ │ │ └── transactions_card.dart │ ├── home │ │ ├── widgets │ │ │ ├── add_send_funds_container.dart │ │ │ ├── send_money_found_user_details_container.dart │ │ │ └── payment_containers.dart │ │ └── screens │ │ │ ├── comming_soon_screen.dart │ │ │ └── fund_wallet_screen.dart │ └── onboarding │ │ └── screens │ │ ├── widgets │ │ └── glassmorphic_card.dart │ │ └── onboarding_screen.dart ├── widgets │ ├── width_space.dart │ ├── height_space.dart │ ├── custom_app_bar.dart │ ├── circular_loader.dart │ ├── border_painter.dart │ ├── pin_input_field.dart │ ├── number_dial_pad.dart │ ├── alert_message.dart │ ├── custom_button.dart │ ├── otp_input_field.dart │ ├── custom_textfield.dart │ └── main_app.dart ├── config │ ├── routes │ │ ├── custom_push_navigators.dart │ │ ├── page_fade_transition.dart │ │ ├── page_slide_transition.dart │ │ └── router.dart │ └── theme │ │ └── theme_manager.dart ├── initialization_screen.dart ├── no_internet_screen.dart └── main.dart ├── .gitignore ├── LICENSE.md ├── test └── widget_test.dart ├── analysis_options.yaml ├── .metadata ├── pubspec.yaml └── README.md /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /assets/icons/add_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/add_icon.png -------------------------------------------------------------------------------- /assets/icons/card_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/card_icon.png -------------------------------------------------------------------------------- /assets/icons/chat_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/chat_icon.png -------------------------------------------------------------------------------- /assets/icons/home_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/home_icon.png -------------------------------------------------------------------------------- /assets/icons/more_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/more_icon.png -------------------------------------------------------------------------------- /assets/icons/send_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/send_icon.png -------------------------------------------------------------------------------- /assets/icons/wifi_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/wifi_icon.png -------------------------------------------------------------------------------- /assets/icons/bills_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/bills_icon.png -------------------------------------------------------------------------------- /assets/icons/budget_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/budget_icon.png -------------------------------------------------------------------------------- /assets/icons/credit_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/credit_icon.png -------------------------------------------------------------------------------- /assets/icons/debit_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/debit_icon.png -------------------------------------------------------------------------------- /assets/icons/info-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/info-circle.png -------------------------------------------------------------------------------- /assets/icons/logout_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/logout_icon.png -------------------------------------------------------------------------------- /assets/icons/mobile_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/mobile_icon.png -------------------------------------------------------------------------------- /assets/images/empty_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/empty_list.png -------------------------------------------------------------------------------- /assets/images/main_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/main_logo.png -------------------------------------------------------------------------------- /assets/fonts/Comfortaa-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/fonts/Comfortaa-Bold.ttf -------------------------------------------------------------------------------- /assets/icons/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/notification.png -------------------------------------------------------------------------------- /assets/icons/profile_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/profile_icon.png -------------------------------------------------------------------------------- /assets/icons/settings_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/settings_icon.png -------------------------------------------------------------------------------- /assets/icons/shopping_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/shopping_icon.png -------------------------------------------------------------------------------- /assets/images/error_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/error_image.png -------------------------------------------------------------------------------- /assets/images/no-internet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/no-internet.png -------------------------------------------------------------------------------- /assets/fonts/Comfortaa-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/fonts/Comfortaa-Medium.ttf -------------------------------------------------------------------------------- /assets/icons/contactless_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/contactless_icon.png -------------------------------------------------------------------------------- /assets/icons/electricity_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/electricity_icon.png -------------------------------------------------------------------------------- /assets/images/card_chip_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/card_chip_image.png -------------------------------------------------------------------------------- /assets/images/gradient_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/gradient_circle.png -------------------------------------------------------------------------------- /assets/fonts/Comfortaa-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/fonts/Comfortaa-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Comfortaa-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/fonts/Comfortaa-SemiBold.ttf -------------------------------------------------------------------------------- /assets/icons/transactions_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/icons/transactions_icon.png -------------------------------------------------------------------------------- /assets/images/Pay Mobile advert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/Pay Mobile advert.png -------------------------------------------------------------------------------- /assets/images/notifications_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/notifications_logo.png -------------------------------------------------------------------------------- /assets/images/pay_mobile_advert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/pay_mobile_advert.png -------------------------------------------------------------------------------- /assets/images/dialog_success_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/dialog_success_image.png -------------------------------------------------------------------------------- /assets/images/pin_feature_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/pin_feature_showcase.png -------------------------------------------------------------------------------- /assets/images/responsive_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/responsive_showcase.png -------------------------------------------------------------------------------- /assets/images/pay_mobile_full_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/pay_mobile_full_stack.png -------------------------------------------------------------------------------- /assets/images/customer_support_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/customer_support_showcase.png -------------------------------------------------------------------------------- /assets/images/forgort_password_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/forgort_password_showcase.png -------------------------------------------------------------------------------- /assets/images/in_app_notification_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/in_app_notification_image.png -------------------------------------------------------------------------------- /assets/images/success_dialogs_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/success_dialogs_showcase.png -------------------------------------------------------------------------------- /assets/images/push_notification_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/push_notification_showcase.png -------------------------------------------------------------------------------- /assets/images/signup_verification_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/signup_verification_showcase.png -------------------------------------------------------------------------------- /assets/images/transaction_details_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/transaction_details_showcase.png -------------------------------------------------------------------------------- /assets/images/username_transfer_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/username_transfer_showcase.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /lib/core/utils/custom_exception_handler.dart: -------------------------------------------------------------------------------- 1 | // import 'dart:io'; 2 | 3 | // httpE() { 4 | // switch (HttpException) { 5 | // case SocketException: 6 | // } 7 | // } 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /assets/images/username_search_success_showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/assets/images/username_search_success_showcase.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/notifications_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/drawable/notifications_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@2x~car.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-60@3x~car.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5@2x~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adedayoniyi/Pay-Mobile-P2P-Money-Transfer-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ios-marketing.png -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/money_transfer_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.pay_mobile_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/features/profile/screens/security_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SecurityScreen extends StatelessWidget { 4 | const SecurityScreen({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Container(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/firebase_app_id_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_generated_by": "FlutterFire CLI", 3 | "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", 4 | "GOOGLE_APP_ID": "1:722376799583:ios:d5a7f4fabc06bc20eef2ea", 5 | "FIREBASE_PROJECT_ID": "pay-mobile-ddab3", 6 | "GCM_SENDER_ID": "722376799583" 7 | } -------------------------------------------------------------------------------- /ios/Runner.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/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/widgets/width_space.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class WidthSpace extends StatelessWidget { 4 | final double width; 5 | const WidthSpace(this.width, {super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return SizedBox( 10 | width: width, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/widgets/height_space.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HeightSpace extends StatelessWidget { 4 | final double height; 5 | const HeightSpace(this.height, {super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return SizedBox( 10 | height: height, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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 | app/upload-keystore.jks 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/config/routes/custom_push_navigators.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void namedNav(BuildContext context, String route) { 4 | Navigator.pushNamed(context, route); 5 | } 6 | 7 | void popNav(BuildContext context) { 8 | Navigator.pop(context); 9 | } 10 | 11 | void namedNavRemoveUntil(BuildContext context, String route) { 12 | Navigator.pushNamedAndRemoveUntil(context, route, (route) => false); 13 | } 14 | -------------------------------------------------------------------------------- /lib/features/auth/providers/auth_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AuthProvider extends ChangeNotifier { 4 | String? deviceToken; 5 | String? emailAddress; 6 | 7 | void setDeviceToken(String? token) { 8 | deviceToken = token; 9 | notifyListeners(); 10 | } 11 | 12 | void setUserEmail(String? email) { 13 | emailAddress = email; 14 | notifyListeners(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/initialization_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/widgets/circular_loader.dart'; 3 | 4 | class InitializationScreen extends StatelessWidget { 5 | const InitializationScreen({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return const Scaffold( 10 | body: SafeArea( 11 | child: CircularLoader(), 12 | ), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 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 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LaunchImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LaunchImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/features/profile/providers/chat_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/features/profile/models/chat_model.dart'; 3 | 4 | class ChatProvider extends ChangeNotifier { 5 | ChatModel _chatModel = ChatModel( 6 | id: "", 7 | chatName: '', 8 | sender: '', 9 | receiver: '', 10 | ); 11 | 12 | //creating getter for user 13 | ChatModel get chat => _chatModel; 14 | 15 | void setChatModel(String chat) { 16 | //data fromjson coming from models/user.dart 17 | _chatModel = ChatModel.fromJson(chat); 18 | notifyListeners(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/features/auth/providers/user_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/features/auth/models/user.dart'; 3 | 4 | class UserProvider extends ChangeNotifier { 5 | User _user = User( 6 | fullname: '', 7 | username: '', 8 | email: '', 9 | password: '', 10 | token: '', 11 | type: '', 12 | id: '', 13 | pin: '', 14 | isVerified: false, 15 | ); 16 | 17 | //creating getter for user 18 | User get user => _user; 19 | 20 | void setUser(String user) { 21 | //data fromjson coming from models/user.dart 22 | _user = User.fromJson(user); 23 | notifyListeners(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/widgets/custom_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 3 | 4 | class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { 5 | final String image; 6 | const CustomAppBar({ 7 | super.key, 8 | required this.image, 9 | }); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return AppBar( 14 | title: Image.asset( 15 | image, 16 | height: heightValue45, 17 | // width: heightValue26, 18 | ), 19 | centerTitle: true, 20 | ); 21 | } 22 | 23 | @override 24 | Size get preferredSize { 25 | return Size.fromHeight(heightValue50); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/config/routes/page_fade_transition.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: overridden_fields 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class PageFadeTransition extends PageRouteBuilder { 6 | @override 7 | final Widget Function(BuildContext, Animation, Animation) 8 | pageBuilder; 9 | @override 10 | final RouteSettings settings; 11 | 12 | PageFadeTransition({required this.pageBuilder, required this.settings}) 13 | : super( 14 | pageBuilder: pageBuilder, 15 | transitionDuration: const Duration(seconds: 1), 16 | transitionsBuilder: (_, animation, __, child) => 17 | FadeTransition(opacity: animation, child: child), 18 | settings: settings, 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.3.15' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | tasks.register("clean", Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /lib/widgets/circular_loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/assets.dart'; 4 | 5 | class CircularLoader extends StatelessWidget { 6 | const CircularLoader({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Stack( 11 | children: [ 12 | Center( 13 | child: Image.asset( 14 | logo, 15 | height: heightValue35, 16 | ), 17 | ), 18 | Center( 19 | child: SizedBox( 20 | height: heightValue70, 21 | width: heightValue70, 22 | child: const CircularProgressIndicator(), 23 | ), 24 | ), 25 | ], 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/config/routes/page_slide_transition.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: overridden_fields 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class PageSlideTransition extends PageRouteBuilder { 6 | @override 7 | final Widget Function(BuildContext, Animation, Animation) 8 | pageBuilder; 9 | @override 10 | final RouteSettings settings; 11 | 12 | PageSlideTransition({required this.pageBuilder, required this.settings}) 13 | : super( 14 | pageBuilder: pageBuilder, 15 | transitionDuration: const Duration(seconds: 1), 16 | transitionsBuilder: (_, animation, __, child) => SlideTransition( 17 | position: Tween( 18 | begin: const Offset(1.0, 0.0), end: const Offset(0.0, 0.0)) 19 | .animate(animation), 20 | child: child, 21 | ), 22 | settings: settings, 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /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 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/config/theme/theme_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | 4 | class ThemeManager { 5 | final darkTheme = ThemeData( 6 | fontFamily: 'Comfortaa', 7 | useMaterial3: true, 8 | colorScheme: const ColorScheme.dark( 9 | primary: primaryAppColor, 10 | background: scaffoldBackgroundColor, 11 | brightness: Brightness.dark, 12 | error: errorColor, 13 | ), 14 | appBarTheme: const AppBarTheme( 15 | backgroundColor: scaffoldBackgroundColor, 16 | surfaceTintColor: transparentColor, 17 | ), 18 | elevatedButtonTheme: ElevatedButtonThemeData( 19 | style: ElevatedButton.styleFrom( 20 | backgroundColor: primaryAppColor, 21 | ), 22 | ), 23 | ); 24 | 25 | final lightTheme = ThemeData( 26 | //To be implemented later if decided 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /lib/core/error/error_handler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:http/http.dart' as http; 5 | import 'package:pay_mobile_app/config/routes/custom_push_navigators.dart'; 6 | import 'package:pay_mobile_app/core/utils/utils.dart'; 7 | 8 | void statusCodeHandler({ 9 | required BuildContext context, 10 | required http.Response response, 11 | required VoidCallback onSuccess, 12 | }) { 13 | switch (response.statusCode) { 14 | case 201: 15 | case 200: 16 | onSuccess(); 17 | break; 18 | case 400: 19 | case 409: 20 | showErrorMessage( 21 | context: context, 22 | title: "Error", 23 | message: jsonDecode(response.body)['message'], 24 | onTap: () => popNav(context), 25 | ); 26 | break; 27 | case 500: 28 | showSnackBar(context, jsonDecode(response.body)); 29 | break; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/core/utils/color_constants.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | const primaryAppColor = Color(0xFFB3E0B8); 5 | const scaffoldBackgroundColor = Color(0xFF141318); 6 | const secondaryAppColor = Color(0xFF141318); 7 | const tertiaryAppColor = Color(0xFF8100D4); 8 | const errorColor = Colors.red; 9 | const transparentColor = Colors.transparent; 10 | const whiteColor = Color(0xFFFFFFFF); 11 | const greyScale900 = Color(0xFF010E0E); 12 | const greyScale850 = Color(0xFF333333); 13 | const greyScale800 = Color(0xFF424242); 14 | const greyScale700 = Color(0xFF616161); 15 | const greyScale600 = Color(0xFF757575); 16 | const greyScale500 = Color(0xFF9E9E9E); 17 | const greyScale400 = Color(0xFFBDBDBD); 18 | const greyScale300 = Color(0xFFE0E0E0); 19 | const greyScale200 = Color(0xFFEEEEEE); 20 | const greyScale150 = Color(0xFFF2F2F2); 21 | const greyScale100 = Color(0xFFF5F5F5); 22 | const greyScale50 = Color(0xFFFAFAFA); 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | 46 | lib/firebase_options.dart 47 | 48 | -------------------------------------------------------------------------------- /lib/features/profile/models/chat_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class ChatModel { 4 | String chatName; 5 | String sender; 6 | String receiver; 7 | String id; 8 | 9 | ChatModel({ 10 | required this.chatName, 11 | required this.sender, 12 | required this.receiver, 13 | required this.id, 14 | }); 15 | 16 | Map toMap() { 17 | return { 18 | 'chatName': chatName, 19 | 'sender': sender, 20 | 'receiver': receiver, 21 | 'id': id, 22 | }; 23 | } 24 | 25 | factory ChatModel.fromMap(Map map) { 26 | return ChatModel( 27 | chatName: map['chatName'] ?? '', 28 | sender: map['sender'] ?? '', 29 | receiver: map['receiver'] ?? '', 30 | id: map['_id'] ?? '', 31 | ); 32 | } 33 | 34 | String toJson() => json.encode(toMap()); 35 | 36 | factory ChatModel.fromJson(String source) => 37 | ChatModel.fromMap(json.decode(source)); 38 | } 39 | -------------------------------------------------------------------------------- /lib/features/transactions/models/transfer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class Transfer { 4 | final String sendersUsername; 5 | final String recipientsUsername; 6 | final int amount; 7 | final String description; 8 | Transfer({ 9 | required this.sendersUsername, 10 | required this.recipientsUsername, 11 | required this.amount, 12 | required this.description, 13 | }); 14 | 15 | Map toMap() { 16 | return { 17 | 'sendersUsername': sendersUsername, 18 | 'recipientsUsername': recipientsUsername, 19 | 'amount': amount, 20 | 'description': description, 21 | }; 22 | } 23 | 24 | factory Transfer.fromMap(Map map) { 25 | return Transfer( 26 | sendersUsername: map['sendersUsername'] ?? '', 27 | recipientsUsername: map['recipientsUsername'] ?? '', 28 | amount: map['amount']?.toInt() ?? 0, 29 | description: map['description'] ?? '', 30 | ); 31 | } 32 | 33 | String toJson() => json.encode(toMap()); 34 | 35 | factory Transfer.fromJson(String source) => 36 | Transfer.fromMap(json.decode(source)); 37 | } 38 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Adedayo 4 | 5 | Pay Mobile - P2P Money Transfer App 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /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:pay_mobile_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 | -------------------------------------------------------------------------------- /lib/widgets/border_painter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | 4 | class BorderPainter extends CustomPainter { 5 | final Color firstColor; 6 | final Color secondColor; 7 | final double borderRadius; 8 | BorderPainter({ 9 | this.firstColor = primaryAppColor, 10 | this.secondColor = tertiaryAppColor, 11 | required this.borderRadius, 12 | }); 13 | 14 | @override 15 | void paint(Canvas canvas, Size size) { 16 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 17 | final gradient = LinearGradient( 18 | begin: Alignment.topLeft, 19 | end: Alignment.bottomRight, 20 | colors: [ 21 | firstColor, 22 | secondColor.withOpacity(0.4), 23 | ], 24 | ); 25 | final paint = Paint() 26 | ..shader = gradient.createShader(rect) 27 | ..style = PaintingStyle.stroke 28 | ..strokeWidth = 2.5; 29 | canvas.drawRRect( 30 | RRect.fromRectAndRadius( 31 | rect, 32 | Radius.circular(borderRadius), 33 | ), 34 | paint, 35 | ); 36 | } 37 | 38 | @override 39 | bool shouldRepaint(covariant CustomPainter oldDelegate) => false; 40 | } 41 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 722376799583-bcaotvdv59pdcjld239n4msn193j3t02.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.722376799583-bcaotvdv59pdcjld239n4msn193j3t02 9 | API_KEY 10 | AIzaSyCKFSKfhaBZJAfLZwrphHnZMjz7yFR9urs 11 | GCM_SENDER_ID 12 | 722376799583 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.dayoniyi.peertopeermoneytransferapp 17 | PROJECT_ID 18 | pay-mobile-ddab3 19 | STORAGE_BUCKET 20 | pay-mobile-ddab3.appspot.com 21 | IS_ADS_ENABLED 22 | 23 | IS_ANALYTICS_ENABLED 24 | 25 | IS_APPINVITE_ENABLED 26 | 27 | IS_GCM_ENABLED 28 | 29 | IS_SIGNIN_ENABLED 30 | 31 | GOOGLE_APP_ID 32 | 1:722376799583:ios:d5a7f4fabc06bc20eef2ea 33 | 34 | -------------------------------------------------------------------------------- /lib/core/utils/assets.dart: -------------------------------------------------------------------------------- 1 | const contactLessIcon = "assets/icons/contactless_icon.png"; 2 | const addIcon = "assets/icons/add_icon.png"; 3 | const sendIcon = "assets/icons/send_icon.png"; 4 | const mobileIcon = "assets/icons/mobile_icon.png"; 5 | const budgetIcon = "assets/icons/budget_icon.png"; 6 | const electricityIcon = "assets/icons/electricity_icon.png"; 7 | const wifiIcon = "assets/icons/wifi_icon.png"; 8 | const billsIcon = "assets/icons/bills_icon.png"; 9 | const moreIcon = "assets/icons/more_icon.png"; 10 | const cardIcon = "assets/icons/card_icon.png"; 11 | const chatIcon = "assets/icons/chat_icon.png"; 12 | const homeIcon = "assets/icons/home_icon.png"; 13 | const transactionsIcon = "assets/icons/transactions_icon.png"; 14 | const profileIcon = "assets/icons/profile_icon.png"; 15 | const creditIcon = "assets/icons/credit_icon.png"; 16 | const debitIcon = "assets/icons/debit_icon.png"; 17 | const infoCircle = "assets/icons/info-circle.png"; 18 | 19 | const mainLogo = "assets/images/main_logo.png"; 20 | const logo = "assets/images/logo.png"; 21 | const gradientCircle = "assets/images/gradient_circle.png"; 22 | const cardChipImage = "assets/images/card_chip_image.png"; 23 | const noInternetImage = "assets/images/no-internet.png.png"; 24 | const notificationsLogo = "assets/images/notifications_logo.png"; 25 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /lib/widgets/pin_input_field.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | 6 | class PinInputField extends StatelessWidget { 7 | final int selectedIndex; 8 | final int index; 9 | final String pin; 10 | 11 | const PinInputField({ 12 | Key? key, 13 | required this.selectedIndex, 14 | required this.index, 15 | required this.pin, 16 | }) : super(key: key); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Container( 21 | alignment: Alignment.center, 22 | height: heightValue80, 23 | width: heightValue80, 24 | margin: EdgeInsets.only(right: value10), 25 | decoration: BoxDecoration( 26 | color: greyScale850, 27 | shape: BoxShape.circle, 28 | border: Border.all( 29 | color: index == selectedIndex ? primaryAppColor : Colors.transparent, 30 | width: 3, 31 | ), 32 | ), 33 | child: pin.length > index 34 | ? Container( 35 | width: value15, 36 | height: value15, 37 | decoration: const BoxDecoration( 38 | color: whiteColor, 39 | shape: BoxShape.circle, 40 | ), 41 | ) 42 | : const SizedBox(), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/core/utils/validators.dart: -------------------------------------------------------------------------------- 1 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 2 | 3 | String? validateField(String? value) { 4 | if (value!.isEmpty) { 5 | return notEmptyError; 6 | } 7 | if (value.length <= 4) { 8 | return 'Must be more than 4 character'; 9 | } else { 10 | return null; 11 | } 12 | } 13 | 14 | String? validateName(String? value) { 15 | if (value!.isEmpty) { 16 | return notEmptyError; 17 | } 18 | if (value.length < 3) { 19 | return 'Name must be more than 2 character'; 20 | } else { 21 | return null; 22 | } 23 | } 24 | 25 | String? validateEmail(String? value) { 26 | String pattern = 27 | r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; 28 | RegExp regex = RegExp(pattern); 29 | if (value!.isEmpty) { 30 | return notEmptyError; 31 | } 32 | if (!regex.hasMatch(value)) { 33 | return fieldNotValidError; 34 | } else { 35 | return null; 36 | } 37 | } 38 | 39 | String? validatePassword(String? value) { 40 | String pattern = 41 | r'^(?!.*(.)\1\1)(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'; 42 | RegExp regex = RegExp(pattern); 43 | if (value!.isEmpty) { 44 | return notEmptyError; 45 | } 46 | if (!regex.hasMatch(value)) { 47 | return fieldNotValidError; 48 | } else { 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/widgets/number_dial_pad.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | import 'package:pay_mobile_app/widgets/border_painter.dart'; 5 | 6 | class NumberDialPad extends StatelessWidget { 7 | final VoidCallback onTap; 8 | final String numberText; 9 | const NumberDialPad({ 10 | Key? key, 11 | required this.onTap, 12 | required this.numberText, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return GestureDetector( 18 | onTap: onTap, 19 | child: Stack( 20 | children: [ 21 | SizedBox( 22 | height: heightValue75, 23 | width: heightValue75, 24 | child: CustomPaint( 25 | painter: BorderPainter(borderRadius: heightValue75), 26 | ), 27 | ), 28 | Positioned( 29 | left: 0, 30 | right: 0, 31 | top: 10, 32 | bottom: 0, 33 | child: Text( 34 | numberText, 35 | textAlign: TextAlign.center, 36 | style: TextStyle( 37 | color: primaryAppColor, 38 | fontSize: heightValue40, 39 | fontWeight: FontWeight.w800, 40 | ), 41 | ), 42 | ), 43 | ], 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "722376799583", 4 | "project_id": "pay-mobile-ddab3", 5 | "storage_bucket": "pay-mobile-ddab3.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:722376799583:android:43a63325c4852c6deef2ea", 11 | "android_client_info": { 12 | "package_name": "com.dayoniyi.peertopeermoneytransferapp" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "722376799583-rmsvi8ala4mbllt9s8m6i46ght3d35k2.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyCyNchM2NEz-4cernri4WZLJq7PJdzYhhw" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "722376799583-rmsvi8ala4mbllt9s8m6i46ght3d35k2.apps.googleusercontent.com", 31 | "client_type": 3 32 | }, 33 | { 34 | "client_id": "722376799583-bcaotvdv59pdcjld239n4msn193j3t02.apps.googleusercontent.com", 35 | "client_type": 2, 36 | "ios_info": { 37 | "bundle_id": "com.dayoniyi.peertopeermoneytransferapp" 38 | } 39 | } 40 | ] 41 | } 42 | } 43 | } 44 | ], 45 | "configuration_version": "1" 46 | } -------------------------------------------------------------------------------- /lib/features/auth/models/user.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class User { 4 | final String fullname; 5 | final String username; 6 | final String email; 7 | final String password; 8 | final String token; 9 | final String type; 10 | final String id; 11 | final String pin; 12 | final bool isVerified; 13 | User({ 14 | required this.fullname, 15 | required this.username, 16 | required this.email, 17 | required this.password, 18 | required this.token, 19 | required this.type, 20 | required this.id, 21 | required this.pin, 22 | required this.isVerified, 23 | }); 24 | 25 | Map toMap() { 26 | return { 27 | 'fullname': fullname, 28 | 'username': username, 29 | 'email': email, 30 | 'password': password, 31 | 'token': token, 32 | 'type': type, 33 | 'id': id, 34 | 'pin': pin, 35 | 'isVerified': isVerified, 36 | }; 37 | } 38 | 39 | factory User.fromMap(Map map) { 40 | return User( 41 | fullname: map['fullname'] ?? '', 42 | username: map['username'] ?? '', 43 | email: map['email'] ?? '', 44 | password: map['password'] ?? '', 45 | token: map['token'] ?? '', 46 | type: map['type'] ?? '', 47 | id: map['_id'] ?? '', 48 | pin: map['pin'] ?? '', 49 | isVerified: map['isVerified'] ?? false, 50 | ); 51 | } 52 | 53 | String toJson() => json.encode(toMap()); 54 | 55 | factory User.fromJson(String source) => User.fromMap(json.decode(source)); 56 | } 57 | -------------------------------------------------------------------------------- /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 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /lib/features/home/widgets/add_send_funds_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | 5 | class AddSendFundsContainers extends StatelessWidget { 6 | final String text; 7 | final String icon; 8 | final VoidCallback onTap; 9 | const AddSendFundsContainers({ 10 | Key? key, 11 | required this.text, 12 | required this.icon, 13 | required this.onTap, 14 | }) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return GestureDetector( 19 | onTap: onTap, 20 | child: Container( 21 | height: heightValue50, 22 | width: heightValue120, 23 | decoration: BoxDecoration( 24 | borderRadius: BorderRadius.circular(heightValue20), 25 | color: primaryAppColor, 26 | ), 27 | child: Row( 28 | mainAxisAlignment: MainAxisAlignment.center, 29 | children: [ 30 | Image.asset( 31 | icon, 32 | height: heightValue35, 33 | color: secondaryAppColor, 34 | ), 35 | SizedBox( 36 | width: value5, 37 | ), 38 | Text( 39 | text, 40 | style: TextStyle( 41 | fontSize: heightValue17, 42 | color: secondaryAppColor, 43 | fontWeight: FontWeight.bold, 44 | ), 45 | ) 46 | ], 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/features/profile/models/message_model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:convert'; 3 | 4 | List messageModelFromJson(String str) => List.from( 5 | json.decode(str).map((x) => MessageModel.fromJson(x))); 6 | 7 | String messageModelToJson(List data) => 8 | json.encode(List.from(data.map((x) => x.toJson()))); 9 | 10 | class MessageModel { 11 | String id; 12 | String sender; 13 | String content; 14 | String receiver; 15 | String chat; 16 | List readBy; 17 | DateTime createdAt; 18 | DateTime updatedAt; 19 | int v; 20 | 21 | MessageModel({ 22 | required this.id, 23 | required this.sender, 24 | required this.content, 25 | required this.receiver, 26 | required this.chat, 27 | required this.readBy, 28 | required this.createdAt, 29 | required this.updatedAt, 30 | required this.v, 31 | }); 32 | 33 | factory MessageModel.fromJson(Map json) => MessageModel( 34 | id: json["_id"], 35 | sender: json["sender"], 36 | content: json["content"], 37 | receiver: json["receiver"], 38 | chat: json["chat"], 39 | readBy: List.from(json["readBy"].map((x) => x)), 40 | createdAt: DateTime.parse(json["createdAt"]), 41 | updatedAt: DateTime.parse(json["updatedAt"]), 42 | v: json["__v"], 43 | ); 44 | 45 | Map toJson() => { 46 | "_id": id, 47 | "sender": sender, 48 | "content": content, 49 | "receiver": receiver, 50 | "chat": chat, 51 | "readBy": List.from(readBy.map((x) => x)), 52 | "createdAt": createdAt.toIso8601String(), 53 | "updatedAt": updatedAt.toIso8601String(), 54 | "__v": v, 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /.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. 5 | 6 | version: 7 | revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 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: 2ad6cd72c040113b47ee9055e722606a490ef0da 17 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 18 | - platform: android 19 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 20 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 21 | - platform: ios 22 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 23 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 24 | - platform: linux 25 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 26 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 27 | - platform: macos 28 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 29 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 30 | - platform: web 31 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 32 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 33 | - platform: windows 34 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 35 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /lib/widgets/alert_message.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: must_be_immutable 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 5 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 6 | import 'package:pay_mobile_app/widgets/custom_button.dart'; 7 | 8 | class AlertMessage extends StatelessWidget { 9 | final String title; 10 | final String message; 11 | final VoidCallback onTap; 12 | final String alertImage; 13 | final Color buttonColor; 14 | const AlertMessage({ 15 | Key? key, 16 | required this.title, 17 | required this.message, 18 | required this.onTap, 19 | this.alertImage = "assets/images/dialog_success_image.png", 20 | this.buttonColor = defaultAppColor, 21 | }) : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return WillPopScope( 26 | onWillPop: () async => false, 27 | child: AlertDialog( 28 | backgroundColor: greyScale850, 29 | surfaceTintColor: greyScale850, 30 | icon: Image.asset( 31 | alertImage, 32 | height: heightValue100, 33 | width: heightValue100, 34 | ), 35 | title: Text( 36 | title, 37 | style: TextStyle( 38 | fontSize: value18, 39 | fontWeight: FontWeight.bold, 40 | ), 41 | ), 42 | content: Text( 43 | message, 44 | textAlign: TextAlign.center, 45 | style: TextStyle( 46 | fontSize: heightValue15, 47 | color: Colors.grey[600], 48 | ), 49 | ), 50 | actions: [ 51 | CustomButton( 52 | buttonText: "Okay", 53 | onTap: onTap, 54 | buttonColor: primaryAppColor, 55 | buttonTextColor: secondaryAppColor, 56 | ) 57 | ], 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/widgets/custom_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | 5 | class CustomButton extends StatelessWidget { 6 | final String buttonText; 7 | final Color buttonColor; 8 | final Color buttonTextColor; 9 | final VoidCallback onTap; 10 | final double borderRadius; 11 | final Color? borderSideColor; 12 | const CustomButton({ 13 | Key? key, 14 | required this.buttonText, 15 | this.buttonColor = primaryAppColor, 16 | required this.buttonTextColor, 17 | required this.onTap, 18 | this.borderRadius = 10, 19 | this.borderSideColor = transparentColor, 20 | }) : super(key: key); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return ElevatedButton( 25 | onPressed: onTap, 26 | style: ButtonStyle( 27 | splashFactory: InkSplash.splashFactory, 28 | overlayColor: MaterialStatePropertyAll( 29 | whiteColor.withOpacity(0.2), 30 | ), 31 | backgroundColor: MaterialStatePropertyAll( 32 | buttonColor, 33 | ), 34 | fixedSize: MaterialStatePropertyAll( 35 | Size(screenWidth, heightValue60), 36 | ), 37 | shape: MaterialStatePropertyAll( 38 | RoundedRectangleBorder( 39 | borderRadius: BorderRadius.circular(borderRadius), 40 | side: BorderSide( 41 | color: borderSideColor!, 42 | width: 1, 43 | ), 44 | ), 45 | )), 46 | child: Center( 47 | child: Text( 48 | buttonText, 49 | style: TextStyle( 50 | fontSize: heightValue19, 51 | color: buttonTextColor, 52 | fontWeight: FontWeight.w900, 53 | ), 54 | ), 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Pay Mobile 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Pay Mobile 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | UIStatusBarHidden 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /lib/core/utils/custom_notifications.dart: -------------------------------------------------------------------------------- 1 | import 'package:awesome_notifications/awesome_notifications.dart'; 2 | import 'package:firebase_messaging/firebase_messaging.dart'; 3 | 4 | class CustomNotifications { 5 | initInfo() async { 6 | AwesomeNotifications().initialize( 7 | null, 8 | [ 9 | NotificationChannel( 10 | playSound: true, 11 | channelKey: 'Pay Mobile', 12 | channelName: 'Pay Mobile', 13 | channelDescription: 'Notification channel for Pay Mobile', 14 | //defaultColor: primaryAppColor, 15 | //ledColor: const Color(0xFF9D50DD), 16 | importance: NotificationImportance.High, 17 | icon: 'resource://drawable/notifications_logo.png', 18 | ) 19 | ], 20 | ); 21 | 22 | FirebaseMessaging.onMessage.listen((RemoteMessage message) async { 23 | print('..................onMessage...................'); 24 | print( 25 | 'onMessage: ${message.notification?.title}/${message.notification?.body}'); 26 | 27 | await AwesomeNotifications().createNotification( 28 | content: NotificationContent( 29 | id: 0, 30 | channelKey: 'Pay Mobile', 31 | title: message.notification?.title, 32 | body: message.notification?.body, 33 | largeIcon: "asset://assets/images/notifications_logo.png", 34 | ), 35 | ); 36 | }); 37 | } 38 | 39 | void requestPermission() async { 40 | FirebaseMessaging messaging = FirebaseMessaging.instance; 41 | 42 | NotificationSettings settings = await messaging.requestPermission( 43 | alert: true, 44 | announcement: false, 45 | badge: true, 46 | carPlay: false, 47 | criticalAlert: false, 48 | provisional: false, 49 | sound: true, 50 | ); 51 | 52 | if (settings.authorizationStatus == AuthorizationStatus.authorized) { 53 | print('User granted permission'); 54 | } else if (settings.authorizationStatus == 55 | AuthorizationStatus.provisional) { 56 | print('User granted provisional permission'); 57 | } else { 58 | print("User declined or has accepted permission"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 16 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /lib/features/transactions/services/transactions_services.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: use_build_context_synchronously, avoid_print, unused_catch_clause 2 | 3 | import 'dart:async'; 4 | import 'dart:convert'; 5 | import 'dart:io'; 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 9 | import 'package:pay_mobile_app/core/utils/utils.dart'; 10 | import 'package:pay_mobile_app/features/transactions/models/transactions.dart'; 11 | import 'package:http/http.dart' as http; 12 | import 'package:pay_mobile_app/features/auth/providers/user_provider.dart'; 13 | import 'package:provider/provider.dart'; 14 | 15 | class TransactionServices { 16 | Future> getAllTransactions({ 17 | required BuildContext context, 18 | }) async { 19 | List transactions = []; 20 | final username = 21 | Provider.of(context, listen: false).user.username; 22 | final userToken = 23 | Provider.of(context, listen: false).user.token; 24 | try { 25 | //showDialogLoader(context); 26 | //CircularProgressIndicator(); 27 | http.Response res = await http.get( 28 | Uri.parse("$uri/api/getTransactions/$username"), 29 | headers: { 30 | "Content-Type": "application/json; charset=UTF-8", 31 | 'x-auth-token': userToken, 32 | }, 33 | ).timeout(const Duration(seconds: 25)); 34 | //Navigator.of(context, rootNavigator: true).pop('dialog'); 35 | 36 | switch (res.statusCode) { 37 | case 200: 38 | transactions = (json.decode(res.body) as List) 39 | .map((data) => Transactions.fromJson(data)) 40 | .toList(); 41 | break; 42 | case 404: 43 | print("No transactions found!!"); 44 | transactions = []; 45 | break; 46 | case 500: 47 | print("Get Transactions Error"); 48 | } 49 | } on TimeoutException catch (e) { 50 | showTimeOutError( 51 | context: context, 52 | ); 53 | } on SocketException catch (e) { 54 | showNoInternetError( 55 | context: context, 56 | ); 57 | } on Error catch (e) { 58 | print('Get All Transactions Error: $e'); 59 | } 60 | return transactions; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/features/transactions/models/transactions.dart: -------------------------------------------------------------------------------- 1 | // To parse this JSON data, do 2 | // 3 | // final transactions = transactionsFromJson(jsonString); 4 | 5 | import 'dart:convert'; 6 | 7 | List transactionsFromJson(String str) => List.from( 8 | json.decode(str).map((x) => Transactions.fromJson(x))); 9 | 10 | String transactionsToJson(List data) => 11 | json.encode(List.from(data.map((x) => x.toJson()))); 12 | 13 | class Transactions { 14 | Transactions({ 15 | required this.trnxType, 16 | required this.purpose, 17 | required this.amount, 18 | required this.username, 19 | required this.reference, 20 | required this.balanceBefore, 21 | required this.balanceAfter, 22 | required this.description, 23 | required this.fullNameTransactionEntity, 24 | required this.createdAt, 25 | required this.updatedAt, 26 | }); 27 | 28 | String trnxType; 29 | String purpose; 30 | int amount; 31 | String username; 32 | String reference; 33 | int balanceBefore; 34 | int balanceAfter; 35 | String fullNameTransactionEntity; 36 | String description; 37 | DateTime createdAt; 38 | DateTime updatedAt; 39 | 40 | factory Transactions.fromJson(Map json) => Transactions( 41 | trnxType: json["trnxType"], 42 | purpose: json["purpose"], 43 | amount: json["amount"], 44 | username: json["username"], 45 | reference: json["reference"], 46 | balanceBefore: json["balanceBefore"], 47 | balanceAfter: json["balanceAfter"], 48 | fullNameTransactionEntity: json["fullNameTransactionEntity"], 49 | description: json["description"], 50 | createdAt: DateTime.parse(json["createdAt"]), 51 | updatedAt: DateTime.parse(json["updatedAt"]), 52 | ); 53 | 54 | Map toJson() => { 55 | "trnxType": trnxType, 56 | "purpose": purpose, 57 | "amount": amount, 58 | "username": username, 59 | "reference": reference, 60 | "balanceBefore": balanceBefore, 61 | "balanceAfter": balanceAfter, 62 | "fullNameTransactionEntity": fullNameTransactionEntity, 63 | "description": description, 64 | "createdAt": createdAt.toIso8601String(), 65 | "updatedAt": updatedAt.toIso8601String(), 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /lib/no_internet_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/assets.dart'; 3 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | import 'package:pay_mobile_app/widgets/custom_button.dart'; 6 | 7 | class NoInternetScreen extends StatelessWidget { 8 | final VoidCallback onTap; 9 | const NoInternetScreen({ 10 | super.key, 11 | required this.onTap, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | body: SafeArea( 18 | child: Stack( 19 | children: [ 20 | Center( 21 | child: Padding( 22 | padding: EdgeInsets.symmetric(horizontal: value30), 23 | child: Column( 24 | mainAxisAlignment: MainAxisAlignment.center, 25 | children: [ 26 | Image.asset( 27 | noInternetImage, 28 | height: heightValue150, 29 | color: Colors.red, 30 | ), 31 | SizedBox( 32 | height: heightValue20, 33 | ), 34 | const Text("Please connect to the internet"), 35 | SizedBox( 36 | height: heightValue50, 37 | ), 38 | CustomButton( 39 | buttonText: "Try Again", 40 | buttonColor: primaryAppColor, 41 | buttonTextColor: whiteColor, 42 | onTap: () { 43 | onTap(); 44 | }) 45 | ], 46 | ), 47 | ), 48 | ), 49 | Align( 50 | alignment: Alignment.topCenter, 51 | child: Padding( 52 | padding: EdgeInsets.only(top: heightValue30), 53 | child: Column( 54 | children: [ 55 | Image.asset( 56 | "assets/images/full_logo.png", 57 | height: value100, 58 | ), 59 | ], 60 | ), 61 | ), 62 | ), 63 | ], 64 | ), 65 | ), 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/widgets/otp_input_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | 5 | class OtpCodeInputField extends StatefulWidget { 6 | final TextEditingController controller; 7 | final Color fillColor; 8 | const OtpCodeInputField({ 9 | Key? key, 10 | required this.controller, 11 | this.fillColor = greyScale850, 12 | }) : super(key: key); 13 | 14 | @override 15 | State createState() => _OtpCodeInputFieldState(); 16 | } 17 | 18 | class _OtpCodeInputFieldState extends State { 19 | @override 20 | Widget build(BuildContext context) { 21 | return TextFormField( 22 | controller: widget.controller, 23 | style: TextStyle(fontSize: heightValue25), 24 | decoration: InputDecoration( 25 | floatingLabelBehavior: FloatingLabelBehavior.never, 26 | border: OutlineInputBorder( 27 | borderSide: BorderSide( 28 | color: widget.fillColor, 29 | ), 30 | borderRadius: BorderRadius.circular(heightValue10), 31 | ), 32 | enabledBorder: OutlineInputBorder( 33 | borderSide: BorderSide( 34 | color: widget.fillColor, 35 | ), 36 | borderRadius: BorderRadius.circular(heightValue10), 37 | ), 38 | focusedBorder: OutlineInputBorder( 39 | borderSide: const BorderSide( 40 | color: primaryAppColor, 41 | width: 2, 42 | ), 43 | borderRadius: BorderRadius.circular(heightValue10), 44 | ), 45 | isDense: true, 46 | counterText: '', 47 | contentPadding: EdgeInsets.all(heightValue10), 48 | filled: true, 49 | fillColor: widget.fillColor, 50 | ), 51 | keyboardType: TextInputType.number, 52 | validator: (value) { 53 | if (value!.isEmpty) { 54 | return notEmptyError; 55 | } 56 | return null; 57 | }, 58 | onChanged: (value) { 59 | if (value.length == 1) { 60 | FocusScope.of(context).nextFocus(); 61 | } 62 | if (value.isEmpty) { 63 | FocusScope.of(context).previousFocus(); 64 | } 65 | }, 66 | autofocus: true, 67 | showCursor: false, 68 | textAlign: TextAlign.center, 69 | maxLength: 1, 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/features/home/screens/comming_soon_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/assets.dart'; 4 | import 'package:pay_mobile_app/widgets/border_painter.dart'; 5 | import 'package:pay_mobile_app/widgets/height_space.dart'; 6 | 7 | class CommingSoonScreen extends StatelessWidget { 8 | static const String route = "/comming-soon-screen"; 9 | const CommingSoonScreen({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: AppBar(), 15 | body: Padding( 16 | padding: EdgeInsets.symmetric(horizontal: value30), 17 | child: Column( 18 | children: [ 19 | HeightSpace(heightValue30), 20 | Image.asset( 21 | mainLogo, 22 | height: heightValue130, 23 | ), 24 | HeightSpace(heightValue20), 25 | Text( 26 | "Comming Soon", 27 | style: TextStyle( 28 | fontSize: value25, 29 | fontWeight: FontWeight.bold, 30 | ), 31 | ), 32 | HeightSpace(heightValue20), 33 | Stack( 34 | children: [ 35 | Positioned( 36 | top: 70, 37 | bottom: 0, 38 | left: 0, 39 | right: 0, 40 | child: Text( 41 | "Coming Soon", 42 | style: TextStyle( 43 | fontWeight: FontWeight.w900, 44 | fontSize: heightValue35, 45 | ), 46 | textAlign: TextAlign.center, 47 | ), 48 | ), 49 | SizedBox( 50 | height: heightValue200, 51 | width: screenWidth, 52 | child: CustomPaint( 53 | painter: BorderPainter(borderRadius: heightValue20), 54 | ), 55 | ), 56 | ], 57 | ), 58 | HeightSpace(heightValue40), 59 | Text( 60 | "This feature is Comming Soon. Stay tuned", 61 | textAlign: TextAlign.center, 62 | style: TextStyle( 63 | fontSize: value25, 64 | fontWeight: FontWeight.bold, 65 | ), 66 | ), 67 | ], 68 | ), 69 | ), 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/features/auth/screens/create_new_password_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/assets.dart'; 4 | import 'package:pay_mobile_app/core/utils/validators.dart'; 5 | import 'package:pay_mobile_app/features/auth/services/auth_service.dart'; 6 | import 'package:pay_mobile_app/widgets/custom_app_bar.dart'; 7 | import 'package:pay_mobile_app/widgets/custom_textfield.dart'; 8 | import 'package:pay_mobile_app/widgets/height_space.dart'; 9 | 10 | class CreateNewPasswordScreen extends StatefulWidget { 11 | static const String route = "/create-new-password"; 12 | const CreateNewPasswordScreen({super.key}); 13 | 14 | @override 15 | State createState() => 16 | _CreateNewPasswordScreenState(); 17 | } 18 | 19 | class _CreateNewPasswordScreenState extends State { 20 | final createNewPasswordFormKey = GlobalKey(); 21 | final AuthService authService = AuthService(); 22 | final TextEditingController passwordController = TextEditingController(); 23 | final TextEditingController confirmPasswordController = 24 | TextEditingController(); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Scaffold( 29 | appBar: const CustomAppBar(image: logo), 30 | body: screenUI(), 31 | ); 32 | } 33 | 34 | void createNewPassword() { 35 | if (createNewPasswordFormKey.currentState!.validate()) { 36 | if (passwordController.text == confirmPasswordController.text) { 37 | authService.createNewPassword( 38 | context: context, 39 | password: passwordController.text, 40 | confirmPassword: confirmPasswordController.text, 41 | ); 42 | } 43 | } 44 | } 45 | 46 | Widget screenUI() { 47 | return Form( 48 | key: createNewPasswordFormKey, 49 | child: Column( 50 | children: [ 51 | HeightSpace(heightValue10), 52 | const Text("Create New Password"), 53 | CustomTextField( 54 | hintText: "Enter your password", 55 | controller: passwordController, 56 | labelText: "Password", 57 | validator: validateField, 58 | ), 59 | CustomTextField( 60 | hintText: "Enter your password", 61 | controller: confirmPasswordController, 62 | labelText: "ConfirmPassword", 63 | validator: validateField, 64 | ), 65 | ], 66 | ), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/features/home/widgets/send_money_found_user_details_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | import 'package:pay_mobile_app/widgets/border_painter.dart'; 5 | 6 | class SendMoneyFoundUserDetailsContainer extends StatelessWidget { 7 | final String circleAvatarText; 8 | final String userFullName; 9 | final String userName; 10 | const SendMoneyFoundUserDetailsContainer({ 11 | Key? key, 12 | required this.circleAvatarText, 13 | required this.userFullName, 14 | required this.userName, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Stack( 20 | children: [ 21 | Container( 22 | width: screenWidth, 23 | height: heightValue80, 24 | decoration: BoxDecoration( 25 | borderRadius: BorderRadius.circular(heightValue20), 26 | ), 27 | child: CustomPaint( 28 | painter: BorderPainter(borderRadius: heightValue20), 29 | )), 30 | Positioned( 31 | top: 0, 32 | bottom: 0, 33 | child: Row( 34 | children: [ 35 | SizedBox( 36 | width: value20, 37 | ), 38 | CircleAvatar( 39 | radius: heightValue25, 40 | child: Center( 41 | child: Text( 42 | circleAvatarText, 43 | style: TextStyle( 44 | fontSize: heightValue23, 45 | fontWeight: FontWeight.bold, 46 | ), 47 | ), 48 | ), 49 | ), 50 | SizedBox( 51 | width: value25, 52 | ), 53 | Column( 54 | mainAxisAlignment: MainAxisAlignment.center, 55 | crossAxisAlignment: CrossAxisAlignment.start, 56 | children: [ 57 | Text( 58 | userFullName, 59 | style: TextStyle( 60 | fontSize: heightValue23, 61 | fontWeight: FontWeight.bold, 62 | ), 63 | ), 64 | Text( 65 | userName, 66 | style: TextStyle( 67 | fontSize: heightValue18, 68 | ), 69 | ), 70 | ], 71 | ), 72 | ], 73 | ), 74 | ), 75 | ], 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/features/profile/widgets/receivers_message_card.dart.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | import 'package:pay_mobile_app/widgets/height_space.dart'; 5 | 6 | class ReceiversMessageCard extends StatelessWidget { 7 | final String message; 8 | final String dateTime; 9 | final String user; 10 | const ReceiversMessageCard({ 11 | Key? key, 12 | required this.message, 13 | required this.dateTime, 14 | required this.user, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Align( 20 | alignment: Alignment.bottomLeft, 21 | child: ConstrainedBox( 22 | constraints: BoxConstraints( 23 | maxWidth: MediaQuery.of(context).size.width - value48, 24 | ), 25 | child: Padding( 26 | padding: EdgeInsets.only( 27 | left: value20, 28 | top: heightValue10, 29 | ), 30 | child: Container( 31 | decoration: BoxDecoration( 32 | borderRadius: BorderRadius.circular(15), 33 | color: const Color(0xFFb9b6c1), 34 | ), 35 | child: Padding( 36 | padding: EdgeInsets.symmetric( 37 | horizontal: heightValue10, vertical: value10), 38 | child: Column( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | Text( 42 | user, 43 | style: TextStyle( 44 | color: Colors.red, 45 | fontSize: heightValue15, 46 | fontWeight: FontWeight.bold, 47 | ), 48 | ), 49 | HeightSpace(heightValue10), 50 | Text( 51 | message, 52 | style: TextStyle( 53 | color: secondaryAppColor, 54 | fontSize: heightValue18, 55 | fontWeight: FontWeight.bold, 56 | ), 57 | ), 58 | HeightSpace(heightValue5), 59 | Text( 60 | dateTime, 61 | style: TextStyle( 62 | color: secondaryAppColor, 63 | fontSize: heightValue13, 64 | ), 65 | ), 66 | ], 67 | ), 68 | ), 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/features/profile/widgets/senders_message_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | import 'package:pay_mobile_app/widgets/height_space.dart'; 5 | 6 | class SendersMessageCard extends StatelessWidget { 7 | final String message; 8 | final String dateTime; 9 | final String user; 10 | 11 | const SendersMessageCard({ 12 | Key? key, 13 | required this.message, 14 | required this.dateTime, 15 | required this.user, 16 | }) : super(key: key); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Align( 21 | alignment: Alignment.bottomRight, 22 | child: ConstrainedBox( 23 | constraints: BoxConstraints( 24 | maxWidth: MediaQuery.of(context).size.width - value48, 25 | ), 26 | child: Padding( 27 | padding: EdgeInsets.only( 28 | right: value20, 29 | top: heightValue15, 30 | ), 31 | child: Container( 32 | decoration: BoxDecoration( 33 | borderRadius: BorderRadius.circular(15), 34 | color: const Color(0xFFb8c2b7), 35 | ), 36 | child: Padding( 37 | padding: EdgeInsets.symmetric( 38 | horizontal: value10, vertical: heightValue10), 39 | child: Column( 40 | crossAxisAlignment: CrossAxisAlignment.start, 41 | children: [ 42 | Text( 43 | user, 44 | style: TextStyle( 45 | color: tertiaryAppColor, 46 | fontSize: heightValue13, 47 | fontWeight: FontWeight.bold, 48 | ), 49 | ), 50 | HeightSpace(heightValue10), 51 | Text( 52 | message, 53 | style: TextStyle( 54 | color: secondaryAppColor, 55 | fontSize: heightValue18, 56 | fontWeight: FontWeight.bold, 57 | ), 58 | ), 59 | HeightSpace(heightValue5), 60 | Text( 61 | dateTime, 62 | style: TextStyle( 63 | color: secondaryAppColor, 64 | fontSize: heightValue13, 65 | ), 66 | ), 67 | ], 68 | ), 69 | ), 70 | ), 71 | ), 72 | ), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/features/home/widgets/payment_containers.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 6 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 7 | import 'package:pay_mobile_app/widgets/border_painter.dart'; 8 | 9 | class PaymentContainers extends StatelessWidget { 10 | final String icon; 11 | final Color color; 12 | final String text; 13 | const PaymentContainers({ 14 | Key? key, 15 | required this.icon, 16 | required this.color, 17 | required this.text, 18 | }) : super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return SizedBox( 23 | child: Stack( 24 | children: [ 25 | // Positioned( 26 | // right: 20, 27 | // bottom: 40, 28 | // child: Image.asset( 29 | // gradientCircle, 30 | // height: heightValue50, 31 | // ), 32 | // ), 33 | Padding( 34 | padding: EdgeInsets.only(right: value10), 35 | child: ClipRRect( 36 | borderRadius: BorderRadius.circular(heightValue20), 37 | child: BackdropFilter( 38 | filter: ImageFilter.blur(sigmaX: 7, sigmaY: 5), 39 | child: Container( 40 | height: heightValue140, 41 | width: heightValue120, 42 | decoration: BoxDecoration( 43 | borderRadius: BorderRadius.circular(heightValue15), 44 | border: Border.all( 45 | color: Colors.white.withOpacity(0.2), 46 | width: 1.0, 47 | ), 48 | ), 49 | child: CustomPaint( 50 | painter: BorderPainter( 51 | firstColor: primaryAppColor, 52 | secondColor: tertiaryAppColor, 53 | borderRadius: heightValue20, 54 | ), 55 | ), 56 | ), 57 | ), 58 | ), 59 | ), 60 | Positioned( 61 | top: 10, 62 | left: 10, 63 | child: Image.asset( 64 | icon, 65 | color: whiteColor, 66 | height: heightValue33, 67 | ), 68 | ), 69 | Positioned( 70 | bottom: 10, 71 | left: 10, 72 | child: Text( 73 | text, 74 | style: TextStyle( 75 | fontWeight: FontWeight.bold, 76 | fontSize: heightValue17, 77 | ), 78 | ), 79 | ) 80 | ], 81 | ), 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/features/transactions/widgets/transaction_details_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | 6 | class TransactionDetailsContainer extends StatelessWidget { 7 | final String label; 8 | final String content; 9 | final Color amountColor; 10 | final bool isRow; 11 | final bool isAmount; 12 | const TransactionDetailsContainer({ 13 | Key? key, 14 | required this.label, 15 | required this.content, 16 | this.amountColor = defaultAppColor, 17 | this.isRow = false, 18 | this.isAmount = false, 19 | }) : super(key: key); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Padding( 24 | padding: EdgeInsets.only(bottom: heightValue15), 25 | child: Container( 26 | width: screenWidth, 27 | height: heightValue100, 28 | decoration: BoxDecoration( 29 | borderRadius: BorderRadius.circular(heightValue10), 30 | color: greyScale850, 31 | ), 32 | child: Padding( 33 | padding: EdgeInsets.only(left: value10), 34 | child: Column( 35 | mainAxisAlignment: MainAxisAlignment.center, 36 | crossAxisAlignment: CrossAxisAlignment.start, 37 | children: [ 38 | Text( 39 | label, 40 | style: TextStyle( 41 | fontSize: heightValue15, 42 | fontWeight: FontWeight.bold, 43 | ), 44 | ), 45 | isRow 46 | ? Row( 47 | children: [ 48 | Image.asset( 49 | "assets/images/dialog_success_image.png", 50 | height: heightValue25, 51 | ), 52 | SizedBox( 53 | width: value5, 54 | ), 55 | Text( 56 | content, 57 | style: TextStyle( 58 | fontSize: heightValue24, 59 | color: primaryAppColor, 60 | ), 61 | ), 62 | ], 63 | ) 64 | : Text( 65 | content, 66 | style: TextStyle( 67 | fontSize: heightValue24, 68 | color: primaryAppColor, 69 | fontWeight: 70 | isAmount ? FontWeight.bold : FontWeight.normal, 71 | ), 72 | ) 73 | ], 74 | ), 75 | ), 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | 35 | android { 36 | compileSdkVersion 33 37 | ndkVersion flutter.ndkVersion 38 | 39 | compileOptions { 40 | sourceCompatibility JavaVersion.VERSION_1_8 41 | targetCompatibility JavaVersion.VERSION_1_8 42 | } 43 | 44 | kotlinOptions { 45 | jvmTarget = '1.8' 46 | } 47 | 48 | sourceSets { 49 | main.java.srcDirs += 'src/main/kotlin' 50 | } 51 | 52 | defaultConfig { 53 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 54 | applicationId "com.dayoniyi.peertopeermoneytransferapp" 55 | // You can update the following values to match your application needs. 56 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 57 | minSdkVersion 21 58 | targetSdkVersion flutter.targetSdkVersion 59 | versionCode flutterVersionCode.toInteger() 60 | versionName flutterVersionName 61 | multiDexEnabled true 62 | } 63 | 64 | signingConfigs { 65 | release { 66 | keyAlias keystoreProperties['keyAlias'] 67 | keyPassword keystoreProperties['keyPassword'] 68 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 69 | storePassword keystoreProperties['storePassword'] 70 | } 71 | } 72 | buildTypes { 73 | release { 74 | signingConfig signingConfigs.release 75 | shrinkResources false 76 | minifyEnabled false 77 | } 78 | } 79 | 80 | } 81 | 82 | flutter { 83 | source '../..' 84 | } 85 | 86 | dependencies { 87 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 88 | implementation 'com.android.support:multidex:1.0.3' 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/features/profile/widgets/profile_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | 6 | class ProfileCard extends StatelessWidget { 7 | final String iconImage; 8 | final String profileOperation; 9 | final String profileOperationDescription; 10 | final VoidCallback onPressed; 11 | const ProfileCard({ 12 | Key? key, 13 | required this.iconImage, 14 | required this.profileOperation, 15 | required this.profileOperationDescription, 16 | required this.onPressed, 17 | }) : super(key: key); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return GestureDetector( 22 | onTap: onPressed, 23 | child: Container( 24 | height: heightValue80, 25 | width: screenWidth, 26 | decoration: BoxDecoration( 27 | borderRadius: BorderRadius.circular(heightValue20), 28 | color: greyScale850, 29 | ), 30 | child: Padding( 31 | padding: EdgeInsets.symmetric(horizontal: value20), 32 | child: Row( 33 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 34 | crossAxisAlignment: CrossAxisAlignment.center, 35 | children: [ 36 | Row( 37 | children: [ 38 | Container( 39 | height: heightValue55, 40 | width: heightValue55, 41 | decoration: BoxDecoration( 42 | borderRadius: BorderRadius.circular(value10), 43 | color: secondaryAppColor, 44 | ), 45 | child: Center( 46 | child: Image.asset( 47 | iconImage, 48 | height: heightValue25, 49 | color: primaryAppColor, 50 | ), 51 | ), 52 | ), 53 | Padding( 54 | padding: EdgeInsets.only(left: value20), 55 | child: Center( 56 | child: Column( 57 | mainAxisAlignment: MainAxisAlignment.center, 58 | crossAxisAlignment: CrossAxisAlignment.start, 59 | children: [ 60 | Text( 61 | profileOperation, 62 | style: TextStyle( 63 | fontWeight: FontWeight.bold, 64 | fontSize: heightValue20, 65 | ), 66 | ), 67 | Text( 68 | profileOperationDescription, 69 | style: TextStyle( 70 | fontSize: heightValue15, 71 | ), 72 | overflow: TextOverflow.ellipsis, 73 | ) 74 | ], 75 | ), 76 | ), 77 | ), 78 | ], 79 | ), 80 | const Icon(Icons.arrow_forward) 81 | ], 82 | ), 83 | ), 84 | ), 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "AppIcon@2x.png", 5 | "idiom": "iphone", 6 | "scale": "2x", 7 | "size": "60x60" 8 | }, 9 | { 10 | "filename": "AppIcon@3x.png", 11 | "idiom": "iphone", 12 | "scale": "3x", 13 | "size": "60x60" 14 | }, 15 | { 16 | "filename": "AppIcon~ipad.png", 17 | "idiom": "ipad", 18 | "scale": "1x", 19 | "size": "76x76" 20 | }, 21 | { 22 | "filename": "AppIcon@2x~ipad.png", 23 | "idiom": "ipad", 24 | "scale": "2x", 25 | "size": "76x76" 26 | }, 27 | { 28 | "filename": "AppIcon-83.5@2x~ipad.png", 29 | "idiom": "ipad", 30 | "scale": "2x", 31 | "size": "83.5x83.5" 32 | }, 33 | { 34 | "filename": "AppIcon-40@2x.png", 35 | "idiom": "iphone", 36 | "scale": "2x", 37 | "size": "40x40" 38 | }, 39 | { 40 | "filename": "AppIcon-40@3x.png", 41 | "idiom": "iphone", 42 | "scale": "3x", 43 | "size": "40x40" 44 | }, 45 | { 46 | "filename": "AppIcon-40~ipad.png", 47 | "idiom": "ipad", 48 | "scale": "1x", 49 | "size": "40x40" 50 | }, 51 | { 52 | "filename": "AppIcon-40@2x~ipad.png", 53 | "idiom": "ipad", 54 | "scale": "2x", 55 | "size": "40x40" 56 | }, 57 | { 58 | "filename": "AppIcon-20@2x.png", 59 | "idiom": "iphone", 60 | "scale": "2x", 61 | "size": "20x20" 62 | }, 63 | { 64 | "filename": "AppIcon-20@3x.png", 65 | "idiom": "iphone", 66 | "scale": "3x", 67 | "size": "20x20" 68 | }, 69 | { 70 | "filename": "AppIcon-20~ipad.png", 71 | "idiom": "ipad", 72 | "scale": "1x", 73 | "size": "20x20" 74 | }, 75 | { 76 | "filename": "AppIcon-20@2x~ipad.png", 77 | "idiom": "ipad", 78 | "scale": "2x", 79 | "size": "20x20" 80 | }, 81 | { 82 | "filename": "AppIcon-29.png", 83 | "idiom": "iphone", 84 | "scale": "1x", 85 | "size": "29x29" 86 | }, 87 | { 88 | "filename": "AppIcon-29@2x.png", 89 | "idiom": "iphone", 90 | "scale": "2x", 91 | "size": "29x29" 92 | }, 93 | { 94 | "filename": "AppIcon-29@3x.png", 95 | "idiom": "iphone", 96 | "scale": "3x", 97 | "size": "29x29" 98 | }, 99 | { 100 | "filename": "AppIcon-29~ipad.png", 101 | "idiom": "ipad", 102 | "scale": "1x", 103 | "size": "29x29" 104 | }, 105 | { 106 | "filename": "AppIcon-29@2x~ipad.png", 107 | "idiom": "ipad", 108 | "scale": "2x", 109 | "size": "29x29" 110 | }, 111 | { 112 | "filename": "AppIcon-60@2x~car.png", 113 | "idiom": "car", 114 | "scale": "2x", 115 | "size": "60x60" 116 | }, 117 | { 118 | "filename": "AppIcon-60@3x~car.png", 119 | "idiom": "car", 120 | "scale": "3x", 121 | "size": "60x60" 122 | }, 123 | { 124 | "filename": "AppIcon~ios-marketing.png", 125 | "idiom": "ios-marketing", 126 | "scale": "1x", 127 | "size": "1024x1024" 128 | } 129 | ], 130 | "info": { 131 | "author": "iconkitchen", 132 | "version": 1 133 | } 134 | } -------------------------------------------------------------------------------- /lib/features/onboarding/screens/widgets/glassmorphic_card.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/assets.dart'; 5 | 6 | class GlassmorphicCard extends StatelessWidget { 7 | final double height; 8 | final double width; 9 | 10 | const GlassmorphicCard( 11 | {super.key, required this.height, required this.width}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Stack( 16 | children: [ 17 | Stack( 18 | children: [ 19 | ClipRRect( 20 | borderRadius: BorderRadius.circular(20), 21 | child: BackdropFilter( 22 | filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), 23 | child: Container( 24 | height: height, 25 | width: width, 26 | decoration: BoxDecoration( 27 | color: Colors.white.withOpacity(0.2), 28 | borderRadius: BorderRadius.circular(20), 29 | border: Border.all( 30 | color: Colors.white.withOpacity(0.2), 31 | ), 32 | ), 33 | child: CustomPaint( 34 | painter: _GradientPainter(), 35 | ), 36 | ), 37 | ), 38 | ), 39 | Positioned( 40 | left: 15, 41 | top: 10, 42 | child: Image.asset( 43 | logo, 44 | height: heightValue50, 45 | ), 46 | ), 47 | Positioned( 48 | top: 93, 49 | left: 20, 50 | child: Image.asset( 51 | cardChipImage, 52 | height: heightValue50, 53 | ), 54 | ), 55 | const Positioned( 56 | top: 100, 57 | bottom: 0, 58 | left: 95, 59 | right: 0, 60 | child: Text( 61 | "1234 5678 9000", 62 | style: TextStyle( 63 | fontSize: 25, 64 | fontWeight: FontWeight.bold, 65 | ), 66 | ), 67 | ), 68 | Positioned( 69 | bottom: 20, 70 | right: 20, 71 | child: Image.asset( 72 | contactLessIcon, 73 | height: heightValue40, 74 | ), 75 | ) 76 | ], 77 | ), 78 | ], 79 | ); 80 | } 81 | } 82 | 83 | class _GradientPainter extends CustomPainter { 84 | @override 85 | void paint(Canvas canvas, Size size) { 86 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 87 | final gradient = LinearGradient( 88 | begin: Alignment.topLeft, 89 | end: Alignment.bottomRight, 90 | colors: [ 91 | const Color(0xFFB3E0B8), 92 | Colors.white.withOpacity(0.2), 93 | ], 94 | ); 95 | final paint = Paint() 96 | ..shader = gradient.createShader(rect) 97 | ..style = PaintingStyle.stroke 98 | ..strokeWidth = 2; 99 | canvas.drawRRect( 100 | RRect.fromRectAndRadius(rect, Radius.circular(heightValue20)), paint); 101 | } 102 | 103 | @override 104 | bool shouldRepaint(covariant CustomPainter oldDelegate) => false; 105 | } 106 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/widgets/custom_textfield.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | 6 | class CustomTextField extends StatelessWidget { 7 | final String? labelText; 8 | final String hintText; 9 | final TextEditingController controller; 10 | final Widget? icon; 11 | final bool obscureText; 12 | final int maxLines; 13 | final TextInputType keyboardType; 14 | final String? errorText; 15 | final void Function(String)? onChanged; 16 | final String? successMessage; 17 | final Icon? prefixIcon; 18 | final bool willContainPrefix; 19 | final List? inputFormatters; 20 | final String? Function(String?)? validator; 21 | const CustomTextField({ 22 | Key? key, 23 | this.labelText = "", 24 | required this.hintText, 25 | required this.controller, 26 | this.icon, 27 | this.obscureText = false, 28 | this.maxLines = 1, 29 | this.keyboardType = TextInputType.name, 30 | this.validator, 31 | this.errorText, 32 | this.onChanged, 33 | this.successMessage, 34 | this.prefixIcon, 35 | this.willContainPrefix = false, 36 | this.inputFormatters, 37 | }) : super(key: key); 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Column( 42 | crossAxisAlignment: CrossAxisAlignment.start, 43 | children: [ 44 | Padding( 45 | padding: EdgeInsets.only(bottom: heightValue5), 46 | child: Text( 47 | labelText!, 48 | style: TextStyle( 49 | fontSize: heightValue15, 50 | fontWeight: FontWeight.bold, 51 | ), 52 | ), 53 | ), 54 | TextFormField( 55 | autofocus: true, 56 | keyboardType: keyboardType, 57 | obscureText: obscureText, 58 | controller: controller, 59 | inputFormatters: inputFormatters, 60 | style: TextStyle(fontSize: heightValue20), 61 | decoration: InputDecoration( 62 | border: OutlineInputBorder( 63 | borderSide: const BorderSide( 64 | color: greyScale850, 65 | width: 2, 66 | ), 67 | borderRadius: BorderRadius.circular(heightValue15), 68 | ), 69 | enabledBorder: OutlineInputBorder( 70 | borderSide: const BorderSide( 71 | color: greyScale850, 72 | width: 2, 73 | ), 74 | borderRadius: BorderRadius.circular(heightValue15), 75 | ), 76 | focusedBorder: OutlineInputBorder( 77 | borderSide: const BorderSide( 78 | color: primaryAppColor, 79 | width: 2, 80 | ), 81 | borderRadius: BorderRadius.circular(heightValue15), 82 | ), 83 | hintText: hintText, 84 | suffixIcon: icon, 85 | errorText: errorText, 86 | prefixIcon: prefixIcon, 87 | filled: true, 88 | fillColor: greyScale850, 89 | contentPadding: EdgeInsets.all(heightValue20), 90 | ), 91 | maxLines: 1, 92 | validator: validator, 93 | onChanged: onChanged, 94 | ), 95 | Text( 96 | successMessage ?? "", 97 | style: const TextStyle(color: Colors.green), 98 | ) 99 | ], 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /lib/widgets/main_app.dart: -------------------------------------------------------------------------------- 1 | //THis the the bottom navigation bar for the entire appplication 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 5 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 6 | import 'package:pay_mobile_app/core/utils/assets.dart'; 7 | import 'package:pay_mobile_app/features/home/screens/home_screen.dart'; 8 | import 'package:pay_mobile_app/features/profile/screens/profile_screen.dart'; 9 | import 'package:pay_mobile_app/features/transactions/screens/all_transactions_screen.dart'; 10 | 11 | class MainApp extends StatefulWidget { 12 | static const String route = '/main-app'; 13 | int currentPage; 14 | MainApp({ 15 | Key? key, 16 | this.currentPage = 0, 17 | }) : super(key: key); 18 | 19 | @override 20 | State createState() => _MainAppState(); 21 | } 22 | 23 | class _MainAppState extends State { 24 | PageController pageController = PageController(); 25 | void updatePage(int page) { 26 | setState(() { 27 | widget.currentPage = page; 28 | }); 29 | pageController.jumpToPage(page); 30 | } 31 | 32 | @override 33 | void initState() { 34 | super.initState(); 35 | pageController = PageController(initialPage: widget.currentPage); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Scaffold( 41 | //extendBody: true, 42 | backgroundColor: Colors.transparent, 43 | body: PageView( 44 | controller: pageController, 45 | physics: const NeverScrollableScrollPhysics(), 46 | onPageChanged: (page) { 47 | setState(() { 48 | widget.currentPage = page; 49 | }); 50 | }, 51 | children: const [ 52 | HomeScreen(), 53 | TransactionsScreen(), 54 | ProfileScreen(), 55 | ], 56 | ), 57 | bottomNavigationBar: BottomNavigationBar( 58 | backgroundColor: primaryAppColor, 59 | elevation: 0, 60 | //fixedColor: primaryAppColor, 61 | type: BottomNavigationBarType.fixed, 62 | onTap: updatePage, 63 | currentIndex: widget.currentPage, 64 | selectedItemColor: secondaryAppColor, 65 | unselectedItemColor: greyScale600, 66 | selectedLabelStyle: const TextStyle( 67 | fontWeight: FontWeight.w900, 68 | color: secondaryAppColor, 69 | ), 70 | items: [ 71 | BottomNavigationBarItem( 72 | icon: Image.asset( 73 | homeIcon, 74 | height: heightValue40, 75 | color: 76 | widget.currentPage == 0 ? secondaryAppColor : greyScale600, 77 | ), 78 | label: "Home", 79 | ), 80 | BottomNavigationBarItem( 81 | icon: Image.asset( 82 | transactionsIcon, 83 | height: heightValue45, 84 | color: widget.currentPage == 1 85 | ? secondaryAppColor 86 | : greyScale600, 87 | ), 88 | label: "Transactions"), 89 | BottomNavigationBarItem( 90 | icon: Image.asset( 91 | profileIcon, 92 | height: heightValue40, 93 | color: widget.currentPage == 2 94 | ? secondaryAppColor 95 | : greyScale600, 96 | ), 97 | label: "Profile"), 98 | ], 99 | )); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/features/auth/screens/forgort_pin_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/config/routes/custom_push_navigators.dart'; 3 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | import 'package:pay_mobile_app/core/utils/assets.dart'; 6 | import 'package:pay_mobile_app/core/utils/validators.dart'; 7 | import 'package:pay_mobile_app/features/auth/screens/forgort_pin_verification_screen.dart'; 8 | import 'package:pay_mobile_app/features/auth/services/auth_service.dart'; 9 | import 'package:pay_mobile_app/features/auth/providers/auth_provider.dart'; 10 | import 'package:pay_mobile_app/widgets/custom_app_bar.dart'; 11 | import 'package:pay_mobile_app/widgets/custom_button.dart'; 12 | import 'package:pay_mobile_app/widgets/custom_textfield.dart'; 13 | import 'package:pay_mobile_app/widgets/height_space.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | class ForgortPinScreen extends StatefulWidget { 17 | static const String route = '/forgort-pin'; 18 | const ForgortPinScreen({super.key}); 19 | 20 | @override 21 | State createState() => _ForgortPinScreenState(); 22 | } 23 | 24 | class _ForgortPinScreenState extends State { 25 | final AuthService authService = AuthService(); 26 | final TextEditingController emailController = TextEditingController(); 27 | final emailFormkey = GlobalKey(); 28 | sendOtp() { 29 | final authProvider = Provider.of(context, listen: false); 30 | 31 | if (emailFormkey.currentState!.validate()) { 32 | authService.sendOtp( 33 | context: context, 34 | email: emailController.text, 35 | onTapDialogButton: () => 36 | namedNav(context, ForgortPinVerificationScreen.route), 37 | sendPurpose: 'forgort-pin', 38 | ); 39 | authProvider.setUserEmail(emailController.text); 40 | } 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return GestureDetector( 46 | onTap: () => FocusScope.of(context).unfocus(), 47 | child: Scaffold( 48 | appBar: const CustomAppBar(image: logo), 49 | body: Padding( 50 | padding: EdgeInsets.symmetric(horizontal: value20), 51 | child: Stack( 52 | children: [ 53 | screenUI(), 54 | Padding( 55 | padding: EdgeInsets.only(bottom: heightValue10), 56 | child: Align( 57 | alignment: Alignment.bottomCenter, 58 | child: CustomButton( 59 | buttonText: "Send Code", 60 | buttonTextColor: secondaryAppColor, 61 | borderRadius: heightValue30, 62 | onTap: () => sendOtp(), 63 | ), 64 | ), 65 | ) 66 | ], 67 | ), 68 | ), 69 | ), 70 | ); 71 | } 72 | 73 | Widget screenUI() { 74 | return Form( 75 | key: emailFormkey, 76 | child: Column( 77 | children: [ 78 | HeightSpace(heightValue10), 79 | Text( 80 | "Forgort Pin", 81 | style: 82 | TextStyle(fontSize: heightValue30, fontWeight: FontWeight.bold), 83 | ), 84 | HeightSpace(heightValue10), 85 | Text( 86 | "Enter the email address connected to your account ", 87 | textAlign: TextAlign.center, 88 | style: TextStyle( 89 | fontSize: heightValue17, 90 | ), 91 | ), 92 | HeightSpace(heightValue10), 93 | CustomTextField( 94 | hintText: "Enter your email", 95 | labelText: "Email", 96 | controller: emailController, 97 | validator: validateEmail, 98 | ) 99 | ], 100 | ), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/features/auth/screens/forgort_password_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/config/routes/custom_push_navigators.dart'; 3 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | import 'package:pay_mobile_app/core/utils/assets.dart'; 6 | import 'package:pay_mobile_app/core/utils/validators.dart'; 7 | import 'package:pay_mobile_app/features/auth/screens/forgort_password_verification_screen.dart'; 8 | import 'package:pay_mobile_app/features/auth/services/auth_service.dart'; 9 | import 'package:pay_mobile_app/features/auth/providers/auth_provider.dart'; 10 | import 'package:pay_mobile_app/widgets/custom_app_bar.dart'; 11 | import 'package:pay_mobile_app/widgets/custom_button.dart'; 12 | import 'package:pay_mobile_app/widgets/custom_textfield.dart'; 13 | import 'package:pay_mobile_app/widgets/height_space.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | class ForgortPasswordScreen extends StatefulWidget { 17 | static const String route = '/forgort-password'; 18 | const ForgortPasswordScreen({super.key}); 19 | 20 | @override 21 | State createState() => _ForgortPasswordScreenState(); 22 | } 23 | 24 | class _ForgortPasswordScreenState extends State { 25 | final AuthService authService = AuthService(); 26 | final TextEditingController emailController = TextEditingController(); 27 | final emailFormkey = GlobalKey(); 28 | sendOtp() { 29 | if (emailFormkey.currentState!.validate()) { 30 | authService.sendOtp( 31 | context: context, 32 | email: emailController.text, 33 | onTapDialogButton: () => namedNavRemoveUntil( 34 | context, ForgortPasswordVerificationScreen.route), 35 | sendPurpose: 'forgort-password', 36 | ); 37 | final authProvider = Provider.of(context, listen: false); 38 | authProvider.setUserEmail(emailController.text); 39 | } 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return GestureDetector( 45 | onTap: () => FocusScope.of(context).unfocus(), 46 | child: Scaffold( 47 | appBar: const CustomAppBar(image: logo), 48 | body: Padding( 49 | padding: EdgeInsets.symmetric(horizontal: value20), 50 | child: Stack( 51 | children: [ 52 | screenUI(), 53 | Padding( 54 | padding: EdgeInsets.only(bottom: heightValue10), 55 | child: Align( 56 | alignment: Alignment.bottomCenter, 57 | child: CustomButton( 58 | buttonText: "Send Code", 59 | buttonTextColor: secondaryAppColor, 60 | borderRadius: heightValue30, 61 | onTap: () => sendOtp(), 62 | ), 63 | ), 64 | ) 65 | ], 66 | ), 67 | ), 68 | ), 69 | ); 70 | } 71 | 72 | Widget screenUI() { 73 | return Form( 74 | key: emailFormkey, 75 | child: Column( 76 | children: [ 77 | HeightSpace(heightValue10), 78 | Text( 79 | "Forgort Password", 80 | style: 81 | TextStyle(fontSize: heightValue30, fontWeight: FontWeight.bold), 82 | ), 83 | HeightSpace(heightValue10), 84 | Text( 85 | "Enter the email address connected to your account ", 86 | textAlign: TextAlign.center, 87 | style: TextStyle( 88 | fontSize: heightValue17, 89 | ), 90 | ), 91 | HeightSpace(heightValue10), 92 | CustomTextField( 93 | hintText: "Enter your email", 94 | labelText: "Email", 95 | controller: emailController, 96 | validator: validateEmail, 97 | ) 98 | ], 99 | ), 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: pay_mobile_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: 2.0.0 20 | 21 | environment: 22 | sdk: ">=2.19.4 <3.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 | flutter: 32 | sdk: flutter 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.2 37 | provider: ^6.0.5 38 | shared_preferences: ^2.0.18 39 | http: ^0.13.5 40 | intl: ^0.18.0 41 | internet_connection_checker: ^1.0.0+1 42 | firebase_core: ^2.14.0 43 | firebase_messaging: ^14.6.4 44 | cloud_firestore: ^4.8.2 45 | socket_io_client: ^2.0.2 46 | awesome_notifications: ^0.6.19 47 | 48 | dev_dependencies: 49 | flutter_test: 50 | sdk: flutter 51 | flutter_native_splash: ^2.2.19 52 | 53 | # The "flutter_lints" package below contains a set of recommended lints to 54 | # encourage good coding practices. The lint set provided by the package is 55 | # activated in the `analysis_options.yaml` file located at the root of your 56 | # package. See that file for information about deactivating specific lint 57 | # rules and activating additional ones. 58 | flutter_lints: ^2.0.0 59 | 60 | flutter_native_splash: 61 | color: "#141318" 62 | image: assets/images/main_logo.png 63 | 64 | # For information on the generic Dart part of this file, see the 65 | # following page: https://dart.dev/tools/pub/pubspec 66 | 67 | # The following section is specific to Flutter packages. 68 | flutter: 69 | # The following line ensures that the Material Icons font is 70 | # included with your application, so that you can use the icons in 71 | # the material Icons class. 72 | uses-material-design: true 73 | 74 | # To add assets to your application, add an assets section, like this: 75 | assets: 76 | - assets/images/ 77 | - assets/fonts/ 78 | - assets/icons/ 79 | 80 | # - images/a_dot_ham.jpeg 81 | 82 | # An image asset can refer to one or more resolution-specific "variants", see 83 | # https://flutter.dev/assets-and-images/#resolution-aware 84 | 85 | # For details regarding adding assets from package dependencies, see 86 | # https://flutter.dev/assets-and-images/#from-packages 87 | 88 | # To add custom fonts to your application, add a fonts section here, 89 | # in this "flutter" section. Each entry in this list should have a 90 | # "family" key with the font family name, and a "fonts" key with a 91 | # list giving the asset and other descriptors for the font. For 92 | # example: 93 | fonts: 94 | - family: Comfortaa 95 | fonts: 96 | - asset: assets/fonts/Comfortaa-Regular.ttf 97 | - asset: assets/fonts/Comfortaa-Medium.ttf 98 | - asset: assets/fonts/Comfortaa-SemiBold.ttf 99 | - asset: assets/fonts/Comfortaa-Bold.ttf 100 | 101 | # - asset: fonts/Schyler-Italic.ttf 102 | # style: italic 103 | # - family: Trajan Pro 104 | # fonts: 105 | # - asset: fonts/TrajanPro.ttf 106 | # - asset: fonts/TrajanPro_Bold.ttf 107 | # weight: 700 108 | # 109 | # For details regarding fonts from package dependencies, 110 | # see https://flutter.dev/custom-fonts/#from-packages 111 | -------------------------------------------------------------------------------- /lib/features/home/screens/fund_wallet_screen.dart: -------------------------------------------------------------------------------- 1 | /*nothing much here. This just asks the user for the amount they will like to add 2 | Then it confirms their pin and updates their balance 3 | You can implement an actual deposit feature if you want*/ 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 7 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 8 | import 'package:pay_mobile_app/core/utils/assets.dart'; 9 | import 'package:pay_mobile_app/features/home/services/home_service.dart'; 10 | import 'package:pay_mobile_app/features/home/widgets/confirm_pin_to_send_money_dialpad.dart'; 11 | import 'package:pay_mobile_app/features/auth/providers/user_provider.dart'; 12 | import 'package:pay_mobile_app/widgets/custom_app_bar.dart'; 13 | import 'package:pay_mobile_app/widgets/custom_button.dart'; 14 | import 'package:pay_mobile_app/widgets/custom_textfield.dart'; 15 | import 'package:provider/provider.dart'; 16 | 17 | class FundWalletScreen extends StatefulWidget { 18 | static const String route = "/add-money"; 19 | const FundWalletScreen({super.key}); 20 | 21 | @override 22 | State createState() => _FundWalletScreenState(); 23 | } 24 | 25 | class _FundWalletScreenState extends State { 26 | final TextEditingController amountController = TextEditingController(); 27 | final HomeService homeService = HomeService(); 28 | 29 | void fundAccontWallet() { 30 | final username = 31 | Provider.of(context, listen: false).user.username; 32 | homeService.fundWallet( 33 | context: context, 34 | username: username, 35 | amount: int.parse(amountController.text), 36 | onLandingOnHomePage: () {}); 37 | } 38 | 39 | @override 40 | void dispose() { 41 | super.dispose(); 42 | amountController.dispose(); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return GestureDetector( 48 | onTap: () => FocusScope.of(context).unfocus(), 49 | child: Scaffold( 50 | appBar: const CustomAppBar( 51 | image: logo, 52 | ), 53 | body: Padding( 54 | padding: EdgeInsets.symmetric(horizontal: value20), 55 | child: Column( 56 | children: [ 57 | Text( 58 | "Add Money", 59 | style: TextStyle( 60 | fontSize: heightValue25, 61 | fontWeight: FontWeight.bold, 62 | ), 63 | ), 64 | Text( 65 | "Enter the amount you will like to add", 66 | style: TextStyle( 67 | fontSize: heightValue24, 68 | fontWeight: FontWeight.bold, 69 | ), 70 | ), 71 | SizedBox( 72 | height: heightValue10, 73 | ), 74 | Row( 75 | mainAxisAlignment: MainAxisAlignment.center, 76 | children: [ 77 | Text( 78 | "Note: max = ", 79 | style: TextStyle( 80 | fontSize: heightValue20, 81 | fontWeight: FontWeight.bold, 82 | ), 83 | ), 84 | Text( 85 | "₦5,000", 86 | style: TextStyle( 87 | fontSize: value15, 88 | color: Colors.red, 89 | fontWeight: FontWeight.bold, 90 | ), 91 | ), 92 | ], 93 | ), 94 | CustomTextField( 95 | keyboardType: TextInputType.number, 96 | hintText: "Enter Amount", 97 | controller: amountController, 98 | ), 99 | SizedBox( 100 | height: heightValue40, 101 | ), 102 | Padding( 103 | padding: EdgeInsets.symmetric(horizontal: value40), 104 | child: CustomButton( 105 | buttonText: "Continue", 106 | buttonColor: primaryAppColor, 107 | buttonTextColor: secondaryAppColor, 108 | onTap: () => showModalBottomSheet( 109 | context: context, 110 | enableDrag: false, 111 | isDismissible: false, 112 | isScrollControlled: true, 113 | constraints: BoxConstraints.loose( 114 | Size(screenWidth, screenHeight), 115 | ), 116 | builder: (context) => ConfirmPinToSendMoneyDialPad( 117 | onSuccess: () { 118 | fundAccontWallet(); 119 | }, 120 | ), 121 | ), 122 | ), 123 | ) 124 | ], 125 | ), 126 | ), 127 | ), 128 | ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /lib/features/onboarding/screens/onboarding_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/assets.dart'; 5 | import 'package:pay_mobile_app/features/auth/screens/login_screen.dart'; 6 | import 'package:pay_mobile_app/features/auth/screens/signup_screen.dart'; 7 | import 'package:pay_mobile_app/features/onboarding/screens/widgets/glassmorphic_card.dart'; 8 | import 'package:pay_mobile_app/widgets/custom_button.dart'; 9 | import 'package:pay_mobile_app/widgets/height_space.dart'; 10 | 11 | class OnBoardingScreen extends StatelessWidget { 12 | static const String route = "/onboarding-screen"; 13 | const OnBoardingScreen({super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Scaffold( 18 | body: SafeArea( 19 | child: Padding( 20 | padding: EdgeInsets.symmetric(horizontal: value10), 21 | child: Stack( 22 | children: [ 23 | Align( 24 | alignment: Alignment.topCenter, 25 | child: Padding( 26 | padding: EdgeInsets.only(top: heightValue30), 27 | child: Column( 28 | children: [ 29 | Image.asset( 30 | mainLogo, 31 | height: heightValue50, 32 | ), 33 | SizedBox( 34 | height: heightValue10, 35 | ), 36 | Text( 37 | "The Best Way to Transfer Money Safely", 38 | style: TextStyle( 39 | fontSize: heightValue15, 40 | color: greyScale500, 41 | ), 42 | ), 43 | ], 44 | ), 45 | ), 46 | ), 47 | Positioned( 48 | top: 150, 49 | right: 0, 50 | child: Image.asset( 51 | gradientCircle, 52 | height: heightValue200, 53 | ), 54 | ), 55 | Column( 56 | mainAxisAlignment: MainAxisAlignment.center, 57 | crossAxisAlignment: CrossAxisAlignment.center, 58 | children: [ 59 | GlassmorphicCard(height: heightValue230, width: 355), 60 | HeightSpace(heightValue35), 61 | Text( 62 | "Send & request payments", 63 | style: TextStyle( 64 | fontWeight: FontWeight.bold, 65 | fontSize: heightValue30, 66 | ), 67 | textAlign: TextAlign.center, 68 | ), 69 | SizedBox( 70 | height: heightValue20, 71 | ), 72 | Text( 73 | "Send or recieve any payments from your accounts with ease and comfort.", 74 | textAlign: TextAlign.center, 75 | style: TextStyle( 76 | fontSize: heightValue18, 77 | color: greyScale500, 78 | ), 79 | ) 80 | ], 81 | ), 82 | Align( 83 | alignment: Alignment.bottomCenter, 84 | child: Padding( 85 | padding: EdgeInsets.only(bottom: heightValue20), 86 | child: Column( 87 | mainAxisAlignment: MainAxisAlignment.end, 88 | children: [ 89 | CustomButton( 90 | borderRadius: heightValue45, 91 | buttonText: "Create Account", 92 | buttonColor: primaryAppColor, 93 | buttonTextColor: scaffoldBackgroundColor, 94 | onTap: () { 95 | Navigator.pushNamed(context, SignUpScreen.route); 96 | }), 97 | SizedBox( 98 | height: heightValue10, 99 | ), 100 | TextButton( 101 | onPressed: () { 102 | Navigator.pushNamed(context, LoginScreen.route); 103 | }, 104 | child: Text( 105 | "Already have an account?", 106 | style: TextStyle( 107 | fontWeight: FontWeight.bold, 108 | fontSize: heightValue18, 109 | ), 110 | ), 111 | ) 112 | ], 113 | ), 114 | ), 115 | ), 116 | ], 117 | ), 118 | ), 119 | ), 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/features/transactions/widgets/transactions_card.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 4 | 5 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 6 | import 'package:pay_mobile_app/core/utils/assets.dart'; 7 | 8 | class TransactionsCard extends StatelessWidget { 9 | final String transactionTypeImage; 10 | final String transactionType; 11 | final String trnxSummary; 12 | final int amount; 13 | final Color amountColorBasedOnTransactionType; 14 | const TransactionsCard({ 15 | Key? key, 16 | required this.transactionTypeImage, 17 | required this.transactionType, 18 | required this.trnxSummary, 19 | required this.amount, 20 | required this.amountColorBasedOnTransactionType, 21 | }) : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Padding( 26 | padding: const EdgeInsets.all(8.0), 27 | child: Stack( 28 | children: [ 29 | Positioned( 30 | bottom: 0, 31 | right: 0, 32 | child: ClipRRect( 33 | borderRadius: BorderRadius.only( 34 | bottomRight: Radius.circular( 35 | heightValue20, 36 | ), 37 | topLeft: Radius.circular( 38 | heightValue25, 39 | ), 40 | ), 41 | child: Image.asset( 42 | gradientCircle, 43 | height: heightValue30, 44 | ), 45 | ), 46 | ), 47 | Container( 48 | height: heightValue80, 49 | width: screenWidth, 50 | decoration: BoxDecoration( 51 | borderRadius: BorderRadius.circular(heightValue20), 52 | color: greyScale850, 53 | ), 54 | child: Padding( 55 | padding: EdgeInsets.symmetric(horizontal: value20), 56 | child: Row( 57 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 58 | crossAxisAlignment: CrossAxisAlignment.center, 59 | children: [ 60 | Container( 61 | height: heightValue50, 62 | width: heightValue50, 63 | decoration: BoxDecoration( 64 | borderRadius: BorderRadius.circular(heightValue15), 65 | color: scaffoldBackgroundColor, 66 | ), 67 | child: Center( 68 | child: Image.asset( 69 | transactionTypeImage, 70 | height: heightValue20, 71 | color: primaryAppColor, 72 | ), 73 | ), 74 | ), 75 | Flexible( 76 | child: Padding( 77 | padding: EdgeInsets.only(left: value20), 78 | child: Column( 79 | mainAxisAlignment: MainAxisAlignment.center, 80 | crossAxisAlignment: CrossAxisAlignment.start, 81 | children: [ 82 | Text( 83 | transactionType, 84 | style: TextStyle( 85 | fontSize: heightValue20, 86 | fontWeight: FontWeight.bold, 87 | ), 88 | ), 89 | Text( 90 | trnxSummary, 91 | style: TextStyle( 92 | fontSize: heightValue17, 93 | ), 94 | overflow: TextOverflow.ellipsis, 95 | ) 96 | ], 97 | ), 98 | ), 99 | ), 100 | Row( 101 | children: [ 102 | Text( 103 | "₦${amountFormatter.format(amount)}", 104 | style: TextStyle( 105 | color: amountColorBasedOnTransactionType, 106 | fontSize: heightValue20, 107 | fontWeight: FontWeight.w700, 108 | ), 109 | ), 110 | SizedBox( 111 | width: value10, 112 | ), 113 | Icon( 114 | Icons.arrow_forward_ios, 115 | size: heightValue20, 116 | ) 117 | ], 118 | ) 119 | ], 120 | ), 121 | ), 122 | ), 123 | 124 | // Positioned( 125 | // left: 0, 126 | // top: 0, 127 | // child: ClipRRect( 128 | // borderRadius: BorderRadius.only( 129 | // bottomRight: Radius.circular( 130 | // heightValue25, 131 | // ), 132 | // topLeft: Radius.circular( 133 | // heightValue20, 134 | // ), 135 | // ), 136 | // child: Container( 137 | // height: heightValue25, 138 | // width: heightValue25, 139 | // decoration: const BoxDecoration( 140 | // color: Color(0xFF5337A5), 141 | // ), 142 | // ), 143 | // ), 144 | // ) 145 | ], 146 | ), 147 | ); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pay Mobile - P2P Money Transfer App 2 | 3 | Pay Mobile 4 | 5 | # Features (Don't forget to give it a star 🌟) 6 | 7 | ## You can perform some actions with the Pay Mobile Web Admin 8 | 9 | #### 1. Pin feature used for authorizing transactions and user login 10 | 11 | In app image 1 12 | 13 | ##### 2. Custom in-app notifications 14 | 15 | In app image 2 16 | 17 | ##### 3. Push notifications for transfers 18 | 19 | In app image 3 20 | 21 | ##### 4. In-app customer service support 22 | 23 | In app image 4 24 | 25 | ##### 5. Success Dialogs 26 | 27 | In app image 5 28 | 29 | ##### 6. Fully responsive(Tablet View) 30 | 31 | In app image 6 32 | 33 | ## New App Features 🌟 34 | 35 | #### 1. Sign Up Verification 36 | 37 | In app image 1 38 | 39 | #### 2. Forgort Password 40 | 41 | In app image 1 42 | 43 | ### QUICK START ⚡ 44 | 45 | #### Visit: Pay Mobile Full Stack to access the full stack code of the software (i.e the Back End and the Web Admin Front End) 46 | 47 | ### Note: The server running this app has already been deployed to render.com, which means you can immediately clone this repo, run it and start using it (i.e The backend is already connected). 48 | 49 | #### Since every username on the app is unique, transfers are performed with usernames. Just enter the `@username` of the user and you can easily transfer funds 50 | 51 | Username Transfer Showcase 52 | 53 | #### After the username is found then transfers can be made 54 | 55 | Username Transfer Showcase 56 | 57 | #### Then tap the transaction to view its details 58 | 59 | Username Transfer Showcase 60 | 61 | #### After cloning don't forget to run: 62 | 63 | ```bash 64 | flutter pub get 65 | ``` 66 | 67 | ## Packages Used 📦 68 | 69 | 1. provider 70 | 2. shared_preferences 71 | 3. http 72 | 4. intl 73 | 5. internet_connrction_checker 74 | 75 | 6. flutter_native_splash 76 | 7. firebase_core 77 | 8. firebase_messaging 78 | 9. cloud_firestore 79 | 10. socket_io_client 80 | 11. awesome_notifications 81 | 82 | #### Here are some test login details of verified users if you don't want to create an account 83 | 84 | ```json 85 | { 86 | "username":"lere", 87 | "pin":"7171", 88 | "password":"test123", 89 | } 90 | { 91 | "username":"johndoe", 92 | "pin":"7171", 93 | "password":"test123", 94 | } 95 | { 96 | "username":"alice", 97 | "pin":"7070", 98 | "password":"test123", 99 | } 100 | { 101 | "username":"bob", 102 | "pin":"7474", 103 | "password":"test123", 104 | } 105 | ``` 106 | 107 | ### If you choose to run it on your own server, visit the Pay Mobile Server Repo 108 | 109 | ## This is the official Nodejs server code that this app is running on Pay Mobile Server 110 | 111 | ## Important 112 | 113 | ### After you are done with configuring the server, dont forget to update the uri in the global_constants.dart file 114 | 115 | 1. Locate lib\core\utils\global_constants.dart and edit line 6 using the server URL you generated or created. Changes will apply globally. Check Below: 116 | 117 | ```dart 118 | 6. const String uri = "https://transfer-dayo-niyi.onrender.com"; 119 | ``` 120 | 121 | To 122 | 123 | ```dart 124 | 6. const String uri = "Your server URL"; 125 | ``` 126 | 127 | ## That's All 🎉🎉🎉 128 | 129 | ## Contributing 130 | 131 | Pull requests are welcome. If you encounter any problem with the app or server, you can open an issue. 132 | 133 | ##### If you liked this project, don't forget to leave a star 🌟. 134 | 135 | ##### Note: As of now, no tests are available 136 | 137 | ## License 138 | 139 | This project is licensed under the MIT License - see the LICENSE file for details. 140 | -------------------------------------------------------------------------------- /lib/config/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/config/routes/page_fade_transition.dart'; 3 | import 'package:pay_mobile_app/config/routes/page_slide_transition.dart'; 4 | import 'package:pay_mobile_app/features/auth/screens/forgort_password_screen.dart'; 5 | import 'package:pay_mobile_app/features/auth/screens/forgort_password_verification_screen.dart'; 6 | import 'package:pay_mobile_app/features/auth/screens/forgort_pin_screen.dart'; 7 | import 'package:pay_mobile_app/features/auth/screens/forgort_pin_verification_screen.dart'; 8 | import 'package:pay_mobile_app/features/auth/screens/login_screen.dart'; 9 | import 'package:pay_mobile_app/features/auth/screens/signup_screen.dart'; 10 | import 'package:pay_mobile_app/features/auth/screens/signup_verification_screen.dart'; 11 | import 'package:pay_mobile_app/features/home/screens/comming_soon_screen.dart'; 12 | import 'package:pay_mobile_app/features/home/screens/fund_wallet_screen.dart'; 13 | import 'package:pay_mobile_app/features/home/screens/home_screen.dart'; 14 | import 'package:pay_mobile_app/features/home/screens/send_money_screen.dart'; 15 | import 'package:pay_mobile_app/features/onboarding/screens/onboarding_screen.dart'; 16 | import 'package:pay_mobile_app/features/profile/screens/change_pin_screen.dart'; 17 | import 'package:pay_mobile_app/features/profile/screens/chat_screen.dart'; 18 | import 'package:pay_mobile_app/features/transactions/screens/all_transactions_screen.dart'; 19 | import 'package:pay_mobile_app/features/transactions/screens/transaction_details_screen.dart'; 20 | import 'package:pay_mobile_app/main.dart'; 21 | import 'package:pay_mobile_app/features/transactions/models/transactions.dart'; 22 | import 'package:pay_mobile_app/widgets/main_app.dart'; 23 | 24 | Route appRoutes(RouteSettings routeSettings) { 25 | switch (routeSettings.name) { 26 | case LoginScreen.route: 27 | return PageFadeTransition( 28 | pageBuilder: (_, __, ___) => const LoginScreen(), 29 | settings: routeSettings, 30 | ); 31 | case SignUpScreen.route: 32 | return PageFadeTransition( 33 | pageBuilder: (_, __, ___) => const SignUpScreen(), 34 | settings: routeSettings, 35 | ); 36 | case SignUpVerificationScreen.route: 37 | return PageSlideTransition( 38 | pageBuilder: (_, __, ___) => const SignUpVerificationScreen(), 39 | settings: routeSettings, 40 | ); 41 | case ForgortPasswordScreen.route: 42 | return PageSlideTransition( 43 | pageBuilder: (_, __, ___) => const ForgortPasswordScreen(), 44 | settings: routeSettings, 45 | ); 46 | case ForgortPasswordVerificationScreen.route: 47 | return PageSlideTransition( 48 | pageBuilder: (_, __, ___) => const ForgortPasswordVerificationScreen(), 49 | settings: routeSettings, 50 | ); 51 | case MainApp.route: 52 | var currentPage = routeSettings.arguments as int; 53 | return PageFadeTransition( 54 | pageBuilder: (_, __, ___) => MainApp( 55 | currentPage: currentPage, 56 | ), 57 | settings: routeSettings, 58 | ); 59 | case SendMoneyScreen.route: 60 | return PageSlideTransition( 61 | pageBuilder: (_, __, ___) => const SendMoneyScreen(), 62 | settings: routeSettings, 63 | ); 64 | case HomeScreen.route: 65 | return PageFadeTransition( 66 | pageBuilder: (_, __, ___) => const HomeScreen(), 67 | settings: routeSettings, 68 | ); 69 | case OnBoardingScreen.route: 70 | return PageFadeTransition( 71 | pageBuilder: (_, __, ___) => const OnBoardingScreen(), 72 | settings: routeSettings, 73 | ); 74 | case TransactionsScreen.route: 75 | return PageFadeTransition( 76 | pageBuilder: (_, __, ___) => const TransactionsScreen(), 77 | settings: routeSettings, 78 | ); 79 | case CommingSoonScreen.route: 80 | return PageSlideTransition( 81 | pageBuilder: (_, __, ___) => const CommingSoonScreen(), 82 | settings: routeSettings, 83 | ); 84 | case ChangeLoginPinScreen.route: 85 | return PageSlideTransition( 86 | pageBuilder: (_, __, ___) => const ChangeLoginPinScreen(), 87 | settings: routeSettings, 88 | ); 89 | case MyApp.route: 90 | return MaterialPageRoute( 91 | builder: (context) => const MyApp(), 92 | ); 93 | case FundWalletScreen.route: 94 | return PageSlideTransition( 95 | pageBuilder: (_, __, ___) => const FundWalletScreen(), 96 | settings: routeSettings, 97 | ); 98 | case TransactionDetailsScreen.route: 99 | var transactions = routeSettings.arguments as Transactions; 100 | return PageFadeTransition( 101 | pageBuilder: (_, __, ___) => TransactionDetailsScreen( 102 | transactions: transactions, 103 | ), 104 | settings: routeSettings, 105 | ); 106 | case ChatScreen.route: 107 | return PageSlideTransition( 108 | pageBuilder: (_, __, ___) => const ChatScreen(), 109 | settings: routeSettings, 110 | ); 111 | case ForgortPinScreen.route: 112 | return PageSlideTransition( 113 | pageBuilder: (_, __, ___) => const ForgortPinScreen(), 114 | settings: routeSettings, 115 | ); 116 | case ForgortPinVerificationScreen.route: 117 | return PageSlideTransition( 118 | pageBuilder: (_, __, ___) => const ForgortPinVerificationScreen(), 119 | settings: routeSettings, 120 | ); 121 | default: 122 | return MaterialPageRoute( 123 | builder: (_) => const Scaffold( 124 | body: Center( 125 | child: Text("This page dosent exist"), 126 | ), 127 | ), 128 | ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | //ATTENTION!!!!! 2 | /* 3 | 1. Please refer to lib/constants/global_constants.dart 4 | 2. Refer to lib/widgets to see custom made widgets that were used throughout the app. 5 | 3. Refer to lib/router.dart to see the navigation routes for the app 6 | 4. Refer to lib/providers/user_provider to see app's state manager for the users data 7 | 5. Refer to money_transfer_server to see the nodejs code controlling the backend 8 | */ 9 | 10 | import 'package:awesome_notifications/awesome_notifications.dart'; 11 | import 'package:firebase_core/firebase_core.dart'; 12 | import 'package:firebase_messaging/firebase_messaging.dart'; 13 | import 'package:flutter/material.dart'; 14 | import 'package:flutter/services.dart'; 15 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 16 | import 'package:pay_mobile_app/config/routes/custom_push_navigators.dart'; 17 | import 'package:pay_mobile_app/config/theme/theme_manager.dart'; 18 | import 'package:pay_mobile_app/core/utils/utils.dart'; 19 | import 'package:pay_mobile_app/core/utils/custom_notifications.dart'; 20 | import 'package:pay_mobile_app/features/auth/screens/login_pin_screen.dart'; 21 | import 'package:pay_mobile_app/features/auth/screens/login_screen.dart'; 22 | import 'package:pay_mobile_app/features/auth/services/auth_service.dart'; 23 | import 'package:pay_mobile_app/features/onboarding/screens/onboarding_screen.dart'; 24 | import 'package:pay_mobile_app/features/profile/providers/chat_provider.dart'; 25 | import 'package:pay_mobile_app/firebase_options.dart'; 26 | import 'package:pay_mobile_app/initialization_screen.dart'; 27 | import 'package:pay_mobile_app/no_internet_screen.dart'; 28 | import 'package:pay_mobile_app/features/auth/providers/auth_provider.dart'; 29 | import 'package:pay_mobile_app/features/auth/providers/user_provider.dart'; 30 | import 'package:pay_mobile_app/config/routes/router.dart'; 31 | import 'package:pay_mobile_app/widgets/main_app.dart'; 32 | import 'package:provider/provider.dart'; 33 | 34 | Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { 35 | print("Handling a background message ${message.messageId}"); 36 | } 37 | 38 | Future main() async { 39 | WidgetsFlutterBinding.ensureInitialized(); 40 | await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); 41 | await FirebaseMessaging.instance.getInitialMessage(); 42 | FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); 43 | runApp( 44 | MultiProvider( 45 | providers: [ 46 | ChangeNotifierProvider( 47 | create: (context) => UserProvider(), 48 | ), 49 | ChangeNotifierProvider( 50 | create: (context) => AuthProvider(), 51 | ), 52 | ChangeNotifierProvider( 53 | create: (context) => ChatProvider(), 54 | ) 55 | ], 56 | child: const MyApp(), 57 | ), 58 | ); 59 | } 60 | 61 | class MyApp extends StatefulWidget { 62 | static const String route = "/my-app"; 63 | const MyApp({super.key}); 64 | 65 | @override 66 | State createState() => _MyAppState(); 67 | } 68 | 69 | class _MyAppState extends State { 70 | final ThemeManager themeManager = ThemeManager(); 71 | final AuthService authService = AuthService(); 72 | final CustomNotifications customNotificatins = CustomNotifications(); 73 | late Future _future; 74 | bool check = true; 75 | 76 | @override 77 | void initState() { 78 | super.initState(); 79 | _future = obtainTokenAndUserData(context); 80 | checkInternetConnection(); 81 | customNotificatins.requestPermission(); 82 | customNotificatins.initInfo(); 83 | AwesomeNotifications().requestPermissionToSendNotifications(); 84 | } 85 | 86 | obtainTokenAndUserData(BuildContext context) async { 87 | await authService.obtainTokenAndUserData(context); 88 | } 89 | 90 | checkInternetConnection() async { 91 | check = await InternetConnectionChecker().hasConnection; 92 | return check; 93 | } 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | SystemChrome.setPreferredOrientations([ 98 | DeviceOrientation.portraitUp, 99 | DeviceOrientation.portraitDown, 100 | ]); 101 | final user = Provider.of(context).user; 102 | return MaterialApp( 103 | theme: themeManager.darkTheme, 104 | darkTheme: themeManager.darkTheme, 105 | debugShowCheckedModeBanner: false, 106 | onGenerateRoute: (settings) => appRoutes(settings), 107 | home: FutureBuilder( 108 | future: _future, 109 | builder: (context, snapshot) { 110 | if (snapshot.connectionState == ConnectionState.done) { 111 | return check == true 112 | ? user.isVerified != false 113 | ? user.token.isNotEmpty 114 | ? user.pin.isNotEmpty 115 | ? const LoginPinScreen() 116 | : MainApp( 117 | currentPage: 0, 118 | ) 119 | : const OnBoardingScreen() 120 | : const LoginScreen() 121 | : NoInternetScreen(onTap: () { 122 | checkInternetConnection(); 123 | obtainTokenAndUserData(context); 124 | if (check == true) { 125 | Navigator.pushNamedAndRemoveUntil( 126 | context, 127 | MyApp.route, 128 | (route) => false, 129 | ); 130 | } else { 131 | showDialogLoader(context); 132 | Future.delayed(const Duration(seconds: 5), () { 133 | popNav(context); 134 | }); 135 | } 136 | }); 137 | } else {} 138 | 139 | return const InitializationScreen(); 140 | }, 141 | ), 142 | ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/core/utils/global_constants.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:intl/intl.dart'; 5 | 6 | const String uri = "https://transfer-dayo-niyi.onrender.com"; 7 | const defaultAppColor = Color(0xFF000000); 8 | var amountFormatter = NumberFormat.decimalPattern('en_US'); 9 | const String notEmptyError = "This field cannot be empty."; 10 | const String fieldNotValidError = "This field is not valid."; 11 | 12 | Color darken(Color color, [double amount = .1]) { 13 | assert(amount >= 0 && amount <= 1); 14 | final hsl = HSLColor.fromColor(color); 15 | final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0)); 16 | return hslDark.toColor(); 17 | } 18 | 19 | var platformDispatcher = PlatformDispatcher.instance; 20 | var screenHeight = platformDispatcher.views.first.physicalSize.height / 21 | platformDispatcher.views.first.devicePixelRatio; 22 | var screenWidth = platformDispatcher.views.first.physicalSize.width / 23 | platformDispatcher.views.first.devicePixelRatio; 24 | var isTablet = (platformDispatcher.views.first.physicalSize.width / 25 | platformDispatcher.views.first.devicePixelRatio) > 26 | 600; 27 | 28 | /*This is the default screen width that is used to obtain the values below 29 | =>428 30 | 31 | This is the default screen height that is used to obtain the values below 32 | =>926 33 | 34 | These are dynamic width values that change if the screen width changes 35 | This is very important for adapting to screen sizes*/ 36 | 37 | //This is mostly used for width value and font sizes 38 | //428 39 | final value3 = screenWidth / 142.66; 40 | final value5 = screenWidth / 85.6; 41 | final value10 = screenWidth / 42.8; 42 | final value11 = screenWidth / 38.90; 43 | final value12 = screenWidth / 35.66; 44 | final value14 = screenWidth / 30.57; 45 | final value15 = screenWidth / 28.5; 46 | final value16 = screenWidth / 26.75; 47 | final value18 = screenWidth / 23.77; 48 | final value20 = screenWidth / 21.4; 49 | final value24 = screenWidth / 17.83; 50 | final value25 = screenWidth / 17.12; 51 | final value30 = screenWidth / 14.26; 52 | final value35 = screenWidth / 12.22; 53 | final value40 = screenWidth / 10.7; 54 | final value45 = screenWidth / 9.51; 55 | final value50 = screenWidth / 8.56; 56 | final value55 = screenWidth / 7.78; 57 | final value48 = screenWidth / 8.91; 58 | final value60 = screenWidth / 7.13; 59 | final value65 = screenWidth / 6.58; 60 | final value70 = screenWidth / 6.11; 61 | final value80 = screenWidth / 5.35; 62 | final value90 = screenWidth / 4.755; 63 | final value100 = screenWidth / 4.28; 64 | final value110 = screenWidth / 3.89; 65 | final value130 = screenWidth / 3.29; 66 | final value145 = screenWidth / 2.95; 67 | final value165 = screenWidth / 2.59; 68 | final value200 = screenWidth / 2.14; 69 | final value220 = screenWidth / 1.94; 70 | 71 | //This is mostly used for height values 72 | //926 73 | final heightValue3 = screenHeight / 308.666; 74 | final heightValue5 = screenHeight / 185.2; 75 | final heightValue10 = screenHeight / 92.6; 76 | final heightValue13 = screenHeight / 71.23; 77 | final heightValue15 = screenHeight / 61.73; 78 | final heightValue17 = screenHeight / 54.47; 79 | final heightValue18 = screenHeight / 51.44; 80 | final heightValue19 = screenHeight / 48.73; 81 | final heightValue20 = screenHeight / 46.3; 82 | final heightValue21 = screenHeight / 44.09; 83 | final heightValue22 = screenHeight / 42.09; 84 | final heightValue23 = screenHeight / 40.26; 85 | final heightValue24 = screenHeight / 38.58; 86 | final heightValue25 = screenHeight / 37.04; 87 | final heightValue27 = screenHeight / 34.29; 88 | final heightValue28 = screenHeight / 33.07; 89 | final heightValue30 = screenHeight / 30.86; 90 | final heightValue32 = screenHeight / 28.93; 91 | final heightValue33 = screenHeight / 28.06; 92 | final heightValue35 = screenHeight / 26.45; 93 | final heightValue36 = screenHeight / 25.72; 94 | final heightValue37 = screenHeight / 25.02; 95 | final heightValue38 = screenHeight / 24.36; 96 | final heightValue40 = screenHeight / 23.15; 97 | final heightValue43 = screenHeight / 21.53; 98 | final heightValue45 = screenHeight / 20.57; 99 | final heightValue50 = screenHeight / 18.52; 100 | final heightValue55 = screenHeight / 16.83; 101 | final heightValue60 = screenHeight / 15.43; 102 | final heightValue65 = screenHeight / 14.24; 103 | final heightValue67 = screenHeight / 13.82; 104 | final heightValue70 = screenHeight / 13.22; 105 | final heightValue75 = screenHeight / 12.34; 106 | final heightValue80 = screenHeight / 11.575; 107 | final heightValue90 = screenHeight / 10.28; 108 | final heightValue93 = screenHeight / 9.95; 109 | final heightValue95 = screenHeight / 9.74; 110 | final heightValue98 = screenHeight / 9.44; 111 | final heightValue100 = screenHeight / 9.26; 112 | final heightValue110 = screenHeight / 8.41; 113 | final heightValue120 = screenHeight / 7.71; 114 | final heightValue130 = screenHeight / 7.12; 115 | final heightValue140 = screenHeight / 6.61; 116 | final heightValue150 = screenHeight / 6.17; 117 | final heightValue160 = screenHeight / 5.78; 118 | final heightValue165 = screenHeight / 5.61; 119 | final heightValue180 = screenHeight / 5.14; 120 | final heightValue200 = screenHeight / 4.63; 121 | final heightValue220 = screenHeight / 4.20; 122 | final heightValue224 = screenHeight / 4.13; 123 | final heightValue225 = screenHeight / 4.11; 124 | final heightValue228 = screenHeight / 4.06; 125 | final heightValue230 = screenHeight / 4.03; 126 | final heightValue240 = screenHeight / 3.85; 127 | final heightValue250 = screenHeight / 3.704; 128 | final heightValue255 = screenHeight / 3.63; 129 | final heightValue260 = screenHeight / 3.56; 130 | final heightValue270 = screenHeight / 3.42; 131 | final heightValue275 = screenHeight / 3.36; 132 | final heightValue280 = screenHeight / 3.30; 133 | 134 | final textFieldBorder = OutlineInputBorder( 135 | borderRadius: BorderRadius.all( 136 | Radius.circular(screenWidth / 52.97), 137 | ), 138 | borderSide: const BorderSide( 139 | color: Colors.transparent, 140 | ), 141 | ); 142 | 143 | final textFieldBorderEnabled = OutlineInputBorder( 144 | borderRadius: BorderRadius.all( 145 | Radius.circular(screenWidth / 52.97), 146 | ), 147 | borderSide: const BorderSide( 148 | color: Colors.transparent, 149 | ), 150 | ); 151 | 152 | final textFieldBorderFocused = OutlineInputBorder( 153 | borderRadius: BorderRadius.all( 154 | Radius.circular(screenWidth / 52.97), 155 | ), 156 | borderSide: const BorderSide( 157 | color: defaultAppColor, 158 | width: 1.0, 159 | ), 160 | ); 161 | -------------------------------------------------------------------------------- /lib/features/profile/screens/chat_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 4 | import 'package:pay_mobile_app/features/profile/models/message_model.dart'; 5 | import 'package:pay_mobile_app/features/profile/providers/chat_provider.dart'; 6 | import 'package:pay_mobile_app/features/profile/services/profile_services.dart'; 7 | import 'package:pay_mobile_app/features/profile/widgets/receivers_message_card.dart.dart'; 8 | import 'package:pay_mobile_app/features/profile/widgets/senders_message_card.dart'; 9 | import 'package:pay_mobile_app/features/auth/providers/user_provider.dart'; 10 | import 'package:pay_mobile_app/widgets/custom_textfield.dart'; 11 | import 'package:provider/provider.dart'; 12 | import 'package:socket_io_client/socket_io_client.dart' as IO; 13 | 14 | class ChatScreen extends StatefulWidget { 15 | static const String route = "/chat-screen"; 16 | const ChatScreen({Key? key}) : super(key: key); 17 | 18 | @override 19 | State createState() => _ChatScreenState(); 20 | } 21 | 22 | class _ChatScreenState extends State { 23 | late final TextEditingController messageController; 24 | late final ScrollController scrollController; 25 | late final IO.Socket socket; 26 | late final StreamController> streamController; 27 | final ProfileServices profileServices = ProfileServices(); 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | final chatProvider = Provider.of(context, listen: false); 33 | print(chatProvider.chat.id); 34 | messageController = TextEditingController(); 35 | scrollController = ScrollController(); 36 | socket = IO.io(uri, { 37 | 'transports': ['websocket'], 38 | 'pingTimeout': 120000, 39 | 'cors': {'origin': uri}, 40 | }); 41 | streamController = StreamController>(); 42 | 43 | socket.emit('join', {'chatId': chatProvider.chat.id}); 44 | socket.on('message', (data) { 45 | streamController.add([MessageModel.fromJson(data)]); 46 | print(streamController); 47 | }); 48 | loadMessages(); 49 | // Future.delayed(Duration.zero, () { 50 | // scrollToBottom(); 51 | // }); 52 | } 53 | 54 | @override 55 | void dispose() { 56 | messageController.dispose(); 57 | scrollController.dispose(); 58 | socket.dispose(); 59 | streamController.close(); 60 | super.dispose(); 61 | } 62 | 63 | Future loadMessages() async { 64 | final messages = await profileServices.getAllMessages(context: context); 65 | streamController.add(messages); 66 | } 67 | 68 | void scrollToBottom() { 69 | scrollController.animateTo( 70 | scrollController.position.maxScrollExtent, 71 | duration: const Duration(milliseconds: 200), 72 | curve: Curves.easeInOut, 73 | ); 74 | } 75 | 76 | void sendMessage() async { 77 | if (messageController.text.isNotEmpty) { 78 | final chatProvider = Provider.of(context, listen: false); 79 | socket.emit('sendMessage', { 80 | 'chatId': chatProvider.chat.id, 81 | 'sender': chatProvider.chat.sender, 82 | 'receiver': chatProvider.chat.receiver, 83 | 'content': messageController.text, 84 | }); 85 | 86 | messageController.clear(); 87 | FocusScope.of(context).unfocus(); 88 | Future.delayed(Duration.zero, () { 89 | scrollToBottom(); 90 | }); 91 | loadMessages(); 92 | } 93 | } 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | return GestureDetector( 98 | onTap: () => FocusScope.of(context).unfocus(), 99 | child: Scaffold( 100 | appBar: AppBar( 101 | title: const Text('Pay Moblie Support'), 102 | centerTitle: true, 103 | ), 104 | body: Column( 105 | children: [ 106 | Expanded( 107 | child: StreamBuilder>( 108 | stream: streamController.stream, 109 | builder: (context, snapshot) { 110 | if (snapshot.hasData) { 111 | final messages = snapshot.data!; 112 | return ListView.builder( 113 | controller: scrollController, 114 | itemCount: messages.length, 115 | itemBuilder: (context, index) { 116 | final message = messages[index]; 117 | final userProvider = 118 | Provider.of(context, listen: false); 119 | if (message.sender == userProvider.user.username) { 120 | return SendersMessageCard( 121 | message: message.content, 122 | dateTime: 123 | '${message.createdAt.day}/${message.createdAt.month}/${message.createdAt.year} ${message.createdAt.hour}:${message.createdAt.minute}', 124 | user: 'You', 125 | ); 126 | } else { 127 | return ReceiversMessageCard( 128 | message: message.content, 129 | dateTime: 130 | '${message.createdAt.day}/${message.createdAt.month}/${message.createdAt.year} ${message.createdAt.hour}:${message.createdAt.minute}', 131 | user: 'Pay Mobile Support', 132 | ); 133 | } 134 | }, 135 | ); 136 | } else { 137 | return const Center(child: CircularProgressIndicator()); 138 | } 139 | }, 140 | ), 141 | ), 142 | Padding( 143 | padding: 144 | const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), 145 | child: Row( 146 | children: [ 147 | Expanded( 148 | child: CustomTextField( 149 | controller: messageController, 150 | hintText: 'Enter your message', 151 | ), 152 | ), 153 | IconButton( 154 | onPressed: sendMessage, 155 | icon: const Icon(Icons.send), 156 | ), 157 | ], 158 | ), 159 | ), 160 | ], 161 | ), 162 | ), 163 | ); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /lib/features/auth/screens/login_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pay_mobile_app/config/routes/custom_push_navigators.dart'; 3 | import 'package:pay_mobile_app/core/utils/color_constants.dart'; 4 | import 'package:pay_mobile_app/core/utils/global_constants.dart'; 5 | import 'package:pay_mobile_app/core/utils/assets.dart'; 6 | import 'package:pay_mobile_app/features/auth/screens/forgort_password_screen.dart'; 7 | import 'package:pay_mobile_app/features/auth/screens/signup_screen.dart'; 8 | import 'package:pay_mobile_app/features/auth/screens/signup_verification_screen.dart'; 9 | import 'package:pay_mobile_app/features/auth/services/auth_service.dart'; 10 | import 'package:pay_mobile_app/features/auth/providers/auth_provider.dart'; 11 | import 'package:pay_mobile_app/features/auth/providers/user_provider.dart'; 12 | import 'package:pay_mobile_app/widgets/custom_app_bar.dart'; 13 | import 'package:pay_mobile_app/widgets/custom_button.dart'; 14 | import 'package:pay_mobile_app/widgets/custom_textfield.dart'; 15 | import 'package:pay_mobile_app/widgets/height_space.dart'; 16 | import 'package:pay_mobile_app/widgets/main_app.dart'; 17 | import 'package:provider/provider.dart'; 18 | 19 | class LoginScreen extends StatefulWidget { 20 | static const route = '/login-screen'; 21 | const LoginScreen({super.key}); 22 | 23 | @override 24 | State createState() => _LoginScreenState(); 25 | } 26 | 27 | class _LoginScreenState extends State { 28 | final TextEditingController usernameController = TextEditingController(); 29 | final TextEditingController passwordController = TextEditingController(); 30 | final AuthService authService = AuthService(); 31 | 32 | bool obscureText = true; 33 | final loginFormKey = GlobalKey(); 34 | 35 | @override 36 | void dispose() { 37 | super.dispose(); 38 | usernameController.dispose(); 39 | passwordController.dispose(); 40 | } 41 | 42 | void loginUser() { 43 | if (loginFormKey.currentState!.validate()) { 44 | authService.loginInUser( 45 | context: context, 46 | username: usernameController.text, 47 | password: passwordController.text, 48 | onLoginSuccess: () { 49 | final userProvider = 50 | Provider.of(context, listen: false).user; 51 | final authProvider = 52 | Provider.of(context, listen: false); 53 | userProvider.isVerified == true 54 | ? Navigator.pushNamedAndRemoveUntil( 55 | context, MainApp.route, (route) => false, 56 | arguments: 0) 57 | : authService.sendOtp( 58 | context: context, 59 | email: userProvider.email, 60 | sendPurpose: 'sign-up-verification', 61 | onTapDialogButton: () => namedNavRemoveUntil( 62 | context, 63 | SignUpVerificationScreen.route, 64 | ), 65 | ); 66 | authProvider.setUserEmail(userProvider.email); 67 | }, 68 | ); 69 | } 70 | } 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | return GestureDetector( 75 | onTap: () => FocusScope.of(context).unfocus(), 76 | child: Scaffold( 77 | appBar: const CustomAppBar(image: logo), 78 | body: SafeArea( 79 | child: Stack( 80 | children: [ 81 | Padding( 82 | padding: const EdgeInsets.symmetric(horizontal: 20), 83 | child: SingleChildScrollView( 84 | child: Column( 85 | children: [ 86 | HeightSpace(heightValue10), 87 | Padding( 88 | padding: EdgeInsets.only(right: value20), 89 | child: Text( 90 | "Login and start transfering", 91 | style: TextStyle( 92 | fontSize: heightValue35, 93 | fontWeight: FontWeight.w900, 94 | height: 1.5, 95 | ), 96 | ), 97 | ), 98 | formUI() 99 | ], 100 | ), 101 | ), 102 | ), 103 | Padding( 104 | padding: EdgeInsets.only( 105 | left: value20, 106 | right: value20, 107 | ), 108 | child: Column( 109 | mainAxisAlignment: MainAxisAlignment.end, 110 | children: [ 111 | CustomButton( 112 | buttonText: "Login", 113 | buttonColor: primaryAppColor, 114 | borderRadius: heightValue30, 115 | buttonTextColor: scaffoldBackgroundColor, 116 | onTap: () { 117 | loginUser(); 118 | }, 119 | ), 120 | TextButton( 121 | onPressed: () { 122 | Navigator.pushNamed(context, SignUpScreen.route); 123 | }, 124 | child: Text( 125 | "Create new account", 126 | style: TextStyle( 127 | fontWeight: FontWeight.bold, 128 | fontSize: heightValue17), 129 | ), 130 | ) 131 | ], 132 | ), 133 | ), 134 | ], 135 | ), 136 | ), 137 | ), 138 | ); 139 | } 140 | 141 | Widget formUI() { 142 | return Form( 143 | key: loginFormKey, 144 | child: Column( 145 | children: [ 146 | CustomTextField( 147 | labelText: "Username", 148 | hintText: "Enter your unique username", 149 | prefixIcon: const Icon(Icons.alternate_email), 150 | willContainPrefix: true, 151 | controller: usernameController, 152 | ), 153 | CustomTextField( 154 | obscureText: obscureText, 155 | labelText: "Password", 156 | hintText: "Enter your password", 157 | controller: passwordController, 158 | icon: InkWell( 159 | onTap: () { 160 | setState(() { 161 | obscureText = !obscureText; 162 | }); 163 | }, 164 | child: Icon( 165 | obscureText ? Icons.visibility : Icons.visibility_off, 166 | ), 167 | ), 168 | ), 169 | TextButton( 170 | onPressed: () => namedNav(context, ForgortPasswordScreen.route), 171 | child: Text( 172 | "Forgort Password?", 173 | style: TextStyle(fontSize: heightValue17), 174 | ), 175 | ) 176 | ], 177 | ), 178 | ); 179 | } 180 | } 181 | --------------------------------------------------------------------------------