├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── ic_launcher-playstore.png │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── vizier │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── ic_launcher_round.png │ │ │ └── logo_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── ic_launcher_round.png │ │ │ └── logo_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── ic_launcher_round.png │ │ │ └── logo_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── ic_launcher_round.png │ │ │ └── logo_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── ic_launcher_round.png │ │ │ └── logo_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── ic_launcher_background.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── fastlane │ ├── Appfile │ └── Fastfile ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ └── Ubuntu │ │ ├── Ubuntu-Bold.ttf │ │ ├── Ubuntu-Light.ttf │ │ ├── Ubuntu-Medium.ttf │ │ └── Ubuntu-Regular.ttf ├── mocks │ ├── finances │ │ ├── investments_image.png │ │ └── my_account_image.png │ ├── portfolio │ │ ├── google_logo.png │ │ ├── netflix_logo.png │ │ ├── nike_logo.png │ │ └── tesla_logo.png │ ├── transactions │ │ ├── apple_logo.png │ │ ├── nike_logo.png │ │ ├── paypal_logo.png │ │ ├── uber_eats_logo.png │ │ └── vodafone_logo.png │ └── user │ │ └── avatar.png └── svgs │ ├── all_money.svg │ ├── baner_boy.svg │ ├── bitcoin.svg │ ├── card_face.svg │ ├── chip.svg │ ├── loans.svg │ ├── manage_money.svg │ ├── money_login.svg │ ├── mortgage.svg │ ├── mortgage_details.svg │ ├── retirement_goals.svg │ ├── retirement_goals_details.svg │ └── stay_focused.svg ├── build.yaml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Gemfile ├── Gemfile.lock ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── ItunesArtwork@2x.png │ │ ├── Contents.json │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.pdf │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h └── fastlane │ ├── Appfile │ └── Fastfile ├── l10n.yaml ├── lib ├── config │ ├── constants │ │ ├── animation_constants.dart │ │ ├── api_constants.dart │ │ └── app_constants.dart │ ├── formatters │ │ ├── card_date_input_formatter.dart │ │ └── card_number_input_formatter.dart │ ├── injector │ │ ├── di.dart │ │ └── module │ │ │ └── common_module.dart │ ├── router │ │ └── app_router.dart │ └── styles │ │ ├── colors │ │ ├── app_colors.dart │ │ └── app_colors_border.dart │ │ ├── decorations │ │ ├── app_decorations.dart │ │ ├── app_decorations_button.dart │ │ └── app_decorations_input.dart │ │ ├── dimensions │ │ ├── app_dimensions.dart │ │ ├── app_dimensions_padding.dart │ │ └── app_dimensions_radius.dart │ │ ├── images │ │ ├── app_images.dart │ │ └── svg_images.dart │ │ ├── text_styles │ │ ├── app_text_styles.dart │ │ └── app_text_styles_button.dart │ │ └── theme │ │ ├── app_theme.dart │ │ ├── dark_theme.dart │ │ └── light_theme.dart ├── cubits │ ├── account_financial_breakdown │ │ ├── account_financial_breakdown_cubit.dart │ │ └── account_financial_breakdown_state.dart │ ├── authentication │ │ ├── authentication_cubit.dart │ │ ├── authentication_state.dart │ │ └── login │ │ │ ├── authentication_login_cubit.dart │ │ │ └── authentication_login_state.dart │ ├── card_form │ │ ├── add │ │ │ ├── card_add_cubit.dart │ │ │ └── card_add_state.dart │ │ ├── card_form_cubit.dart │ │ └── card_form_state.dart │ ├── chart_tabs │ │ ├── chart_tab.dart │ │ ├── chart_tabs_cubit.dart │ │ └── chart_tabs_state.dart │ ├── company_listing │ │ ├── details │ │ │ ├── company_listing_details_cubit.dart │ │ │ └── company_listing_details_state.dart │ │ └── history │ │ │ ├── company_listing_history_cubit.dart │ │ │ └── company_listing_history_state.dart │ ├── financial_history │ │ ├── financial_history_cubit.dart │ │ └── financial_history_state.dart │ ├── goals │ │ ├── goals_cubit.dart │ │ └── goals_state.dart │ ├── my_finances │ │ ├── my_finances_cubit.dart │ │ └── my_finances_state.dart │ ├── my_portfolio │ │ ├── my_portfolio_cubit.dart │ │ └── my_portfolio_state.dart │ ├── offers │ │ ├── details │ │ │ ├── offer_details_cubit.dart │ │ │ └── offer_details_state.dart │ │ └── list │ │ │ ├── offer_list_cubit.dart │ │ │ └── offer_list_state.dart │ ├── portfolio_watchlist │ │ ├── portfolio_watchlist_cubit.dart │ │ └── portfolio_watchlist_state.dart │ ├── retirement_plan │ │ ├── retirement_plan_cubit.dart │ │ └── retirement_plan_state.dart │ ├── transaction_history │ │ ├── transaction_history_cubit.dart │ │ └── transaction_history_state.dart │ ├── user │ │ ├── notifications │ │ │ ├── user_notifications_cubit.dart │ │ │ └── user_notifications_state.dart │ │ ├── user_cubit.dart │ │ └── user_state.dart │ └── wallet │ │ ├── wallet_cubit.dart │ │ └── wallet_state.dart ├── data │ ├── data_sources │ │ ├── authentication │ │ │ ├── authentication_data_source.dart │ │ │ └── dummy_authentication_data_source.dart │ │ ├── finances │ │ │ ├── dummy_finances_data_source.dart │ │ │ └── finances_data_source.dart │ │ ├── offers │ │ │ ├── dummy_offers_data_source.dart │ │ │ └── offers_data_source.dart │ │ ├── portfolio │ │ │ ├── dummy_portfolio_data_source.dart │ │ │ └── portfolio_data_source.dart │ │ ├── user │ │ │ ├── dummy_user_data_source.dart │ │ │ └── user_data_source.dart │ │ └── wallet │ │ │ ├── dummy_wallet_data_source.dart │ │ │ └── wallet_data_source.dart │ ├── errors │ │ └── app_error.dart │ ├── factory │ │ └── chart_factory.dart │ ├── models │ │ ├── account │ │ │ ├── account_model.dart │ │ │ └── breakdown │ │ │ │ └── account_breakdown_model.dart │ │ ├── company_asset │ │ │ └── company_asset_model.dart │ │ ├── company_listing │ │ │ ├── details │ │ │ │ └── company_listing_details_item_model.dart │ │ │ └── history │ │ │ │ ├── company_listing_history_model.dart │ │ │ │ ├── data │ │ │ │ └── company_listing_history_data_model.dart │ │ │ │ └── value │ │ │ │ └── company_listing_history_value_model.dart │ │ ├── finances_overview │ │ │ └── finances_overview_model.dart │ │ ├── financial_history │ │ │ ├── financial_history_model.dart │ │ │ └── history_data │ │ │ │ └── financial_history_data_model.dart │ │ ├── goal │ │ │ └── goal_model.dart │ │ ├── offer │ │ │ ├── offer_model.dart │ │ │ ├── offer_type.dart │ │ │ └── slider_item │ │ │ │ ├── offer_slider_model.dart │ │ │ │ └── offer_slider_type.dart │ │ ├── portfolio_overview │ │ │ └── portfolio_overview_model.dart │ │ ├── retirement_plan │ │ │ ├── data │ │ │ │ └── retirement_plan_data_model.dart │ │ │ └── retirement_plan_model.dart │ │ ├── transaction │ │ │ ├── category │ │ │ │ └── transaction_category_model.dart │ │ │ └── transaction_model.dart │ │ ├── user │ │ │ └── user_model.dart │ │ └── wallet │ │ │ └── wallet_model.dart │ ├── repositories │ │ ├── authentication │ │ │ ├── authentication_repository.dart │ │ │ └── data_authentication_repository.dart │ │ ├── finances │ │ │ ├── data_finances_repository.dart │ │ │ └── finances_repository.dart │ │ ├── offers │ │ │ ├── data_offers_repository.dart │ │ │ └── offers_repository.dart │ │ ├── portfolio │ │ │ ├── data_portfolio_repository.dart │ │ │ └── portfolio_repository.dart │ │ ├── user │ │ │ ├── data_user_repository.dart │ │ │ └── user_repository.dart │ │ └── wallet │ │ │ ├── data_wallet_repository.dart │ │ │ └── wallet_repository.dart │ ├── requests │ │ └── authentication │ │ │ └── authentication_request.dart │ ├── responses │ │ ├── authentication │ │ │ └── authentication_response.dart │ │ ├── response_status.dart │ │ └── user │ │ │ └── notifications │ │ │ └── user_notifications_response.dart │ └── storages │ │ ├── common_storage.dart │ │ ├── secure_storage.dart │ │ ├── storage.dart │ │ ├── storage_keys.dart │ │ └── token_storage.dart ├── extensions │ ├── core_extensions.dart │ ├── extensions.dart │ ├── model_extensions.dart │ └── ui_extensions.dart ├── l10n │ ├── app_en.arb │ └── app_loc.dart ├── main.dart ├── mocks │ ├── factories │ │ ├── duration_factory.dart │ │ ├── transaction_category_factory.dart │ │ └── transaction_factory.dart │ ├── finances_mocks.dart │ ├── mock_factory.dart │ ├── offers_mocks.dart │ ├── portfolio_mocks.dart │ ├── user_mocks.dart │ └── wallet_mocks.dart ├── ui │ ├── app_coordinator.dart │ ├── main_app.dart │ ├── modals │ │ ├── alert_dialog │ │ │ ├── adaptive_alert_dialog.dart │ │ │ ├── adaptive_alert_dialog_action.dart │ │ │ └── adaptive_alert_dialog_factory.dart │ │ └── bottom_actions_sheet │ │ │ ├── bottom_actions_sheet.dart │ │ │ ├── bottom_actions_sheet_factory.dart │ │ │ └── widget │ │ │ └── bottom_sheet_action_cell.dart │ ├── models │ │ ├── card_account_type.dart │ │ ├── chart_multi_pie_item.dart │ │ └── chart_multi_pie_section.dart │ ├── pages │ │ ├── app │ │ │ └── main_app_page.dart │ │ ├── authentication │ │ │ └── login │ │ │ │ ├── authentication_login_page.dart │ │ │ │ └── widgets │ │ │ │ └── login_header.dart │ │ ├── bottom_navigation │ │ │ ├── bottom_navigation_item.dart │ │ │ └── bottom_navigation_page.dart │ │ ├── company_listing_details │ │ │ ├── company_listing_page.dart │ │ │ └── widgets │ │ │ │ ├── company_listing_assets.dart │ │ │ │ ├── company_listing_buttons.dart │ │ │ │ ├── company_listing_footer.dart │ │ │ │ └── company_listing_history_chart.dart │ │ ├── credit_card │ │ │ ├── form │ │ │ │ ├── credit_card_form.dart │ │ │ │ └── credit_card_form_page.dart │ │ │ └── overview │ │ │ │ ├── credit_card_overview.dart │ │ │ │ └── widgets │ │ │ │ ├── credit_card_list.dart │ │ │ │ └── credit_card_transactions_list.dart │ │ ├── finances │ │ │ ├── finances_page.dart │ │ │ └── widgets │ │ │ │ ├── finances_header.dart │ │ │ │ ├── finances_history_chart.dart │ │ │ │ └── finances_status_summary.dart │ │ ├── financial_breakdown │ │ │ ├── financial_breakdown_page.dart │ │ │ ├── financial_breakdown_page_arguments.dart │ │ │ └── widgets │ │ │ │ ├── financial_breakdown_chart.dart │ │ │ │ ├── financial_breakdown_transaction_cell.dart │ │ │ │ └── financial_breakdown_transactions_summary.dart │ │ ├── home │ │ │ ├── add │ │ │ │ ├── home_add_content.dart │ │ │ │ └── widgets │ │ │ │ │ ├── home_add_accounts_section.dart │ │ │ │ │ ├── home_add_action_cell.dart │ │ │ │ │ └── home_add_other_services_section.dart │ │ │ ├── home_page.dart │ │ │ ├── home_tab_item.dart │ │ │ ├── my_future │ │ │ │ ├── home_my_future_content.dart │ │ │ │ └── widgets │ │ │ │ │ ├── home_my_future_assets_header.dart │ │ │ │ │ └── home_my_future_retirement_chart.dart │ │ │ ├── my_wallet │ │ │ │ ├── home_my_wallet_content.dart │ │ │ │ └── widgets │ │ │ │ │ ├── home_my_wallet_footer_banner.dart │ │ │ │ │ ├── home_my_wallet_goal_cell.dart │ │ │ │ │ ├── home_my_wallet_goals_section.dart │ │ │ │ │ └── home_my_wallet_summary.dart │ │ │ └── widgets │ │ │ │ ├── home_app_bar.dart │ │ │ │ └── home_tab_bar.dart │ │ ├── offers │ │ │ ├── details │ │ │ │ ├── offer_details_page.dart │ │ │ │ └── widgets │ │ │ │ │ ├── offer_details_header.dart │ │ │ │ │ ├── offer_details_slider.dart │ │ │ │ │ ├── offer_details_sliders.dart │ │ │ │ │ └── offer_details_summary.dart │ │ │ ├── offers_page.dart │ │ │ └── widgets │ │ │ │ └── offer_cell.dart │ │ ├── onboarding │ │ │ ├── cubit │ │ │ │ ├── onboarding_cubit.dart │ │ │ │ └── onboarding_state.dart │ │ │ ├── onboarding_item.dart │ │ │ ├── onboarding_page.dart │ │ │ └── widgets │ │ │ │ └── onboarding_item_content.dart │ │ ├── portfolio │ │ │ ├── portfolio_page.dart │ │ │ └── widgets │ │ │ │ ├── portfolio_digital_currencies_banner.dart │ │ │ │ ├── portfolio_my_watchlist_cell.dart │ │ │ │ └── portfolio_my_watchlist_section.dart │ │ └── profile │ │ │ ├── profile_page.dart │ │ │ └── widgets │ │ │ ├── profile_action_button.dart │ │ │ ├── profile_app_bar.dart │ │ │ ├── profile_header.dart │ │ │ └── profile_switch_button.dart │ └── widgets │ │ ├── account_summary_cell.dart │ │ ├── adaptive │ │ ├── adaptive_app_bar.dart │ │ ├── adaptive_button.dart │ │ ├── adaptive_loading_button.dart │ │ └── adaptive_text_field.dart │ │ ├── animated_expand.dart │ │ ├── animated_flip.dart │ │ ├── animated_progress_bar.dart │ │ ├── animated_text.dart │ │ ├── arrow_change_value.dart │ │ ├── asset_performance_item.dart │ │ ├── chart_container.dart │ │ ├── chart_tabs_bar.dart │ │ ├── credit_card │ │ ├── animated_credit_card.dart │ │ ├── credit_card_back.dart │ │ ├── credit_card_container.dart │ │ ├── credit_card_cvv.dart │ │ ├── credit_card_expiry.dart │ │ ├── credit_card_front.dart │ │ └── credit_card_number.dart │ │ ├── legend │ │ ├── legends.dart │ │ └── models │ │ │ ├── legend.dart │ │ │ ├── legend_item.dart │ │ │ └── legend_settings.dart │ │ ├── performance_percentage_box.dart │ │ ├── progress_bar.dart │ │ ├── ripple_remover.dart │ │ ├── section_header.dart │ │ ├── transaction_cell.dart │ │ └── unfocus_keyboard_outside.dart └── utils │ ├── adaptive_widget_util.dart │ ├── currency_formatter_util.dart │ ├── date_formatter_util.dart │ ├── dummy_util.dart │ ├── percentage_formatter_util.dart │ └── validation │ ├── validation_builder.dart │ └── validators_util.dart ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | *.freezed.dart 34 | *.g.dart 35 | *.config.dart 36 | *.gr.dart 37 | 38 | # Web related 39 | lib/generated_plugin_registrant.dart 40 | 41 | # Symbolication related 42 | app.*.symbols 43 | 44 | # Obfuscation related 45 | app.*.map.json 46 | 47 | # Android Studio will place build artifacts here 48 | /android/app/debug 49 | /android/app/profile 50 | /android/app/release 51 | 52 | #FVM 53 | .fvm/ 54 | 55 | #Generated 56 | *.freezed* 57 | *.g* 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Merixstudio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | 3 | fvm_run_build_runner: ## Rebuilds all auto generated Dart files 4 | fvm flutter pub run build_runner build --delete-conflicting-outputs 5 | 6 | run_build_runner: ## Rebuilds all auto generated Dart files 7 | flutter pub run build_runner build --delete-conflicting-outputs 8 | 9 | help: 10 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lint/analysis_options.yaml 2 | 3 | analyzer: 4 | exclude: 5 | - "**/*.g.dart" 6 | - "**/*.freezed.dart" 7 | - "**/app_icons.dart" 8 | - "**/*.config.dart" 9 | - "**/*.gr.dart" 10 | errors: 11 | invalid_annotation_target: ignore 12 | 13 | linter: 14 | rules: 15 | always_use_package_imports: true 16 | close_sinks: true 17 | literal_only_boolean_expressions: true 18 | avoid_returning_this: true 19 | always_put_control_body_on_new_line: true 20 | prefer_foreach: true 21 | prefer_single_quotes: true 22 | lowercase_with_underscores: true 23 | use_to_and_as_if_applicable: true 24 | use_named_constants: true 25 | use_is_even_rather_than_modulo: true 26 | use_if_null_to_convert_nulls_to_bools: true 27 | unawaited_futures: true 28 | prefer_null_aware_method_calls: true 29 | avoid_multiple_declarations_per_line: true 30 | avoid_double_and_int_checks: true 31 | always_put_required_named_parameters_first: true 32 | only_throw_errors: true 33 | cascade_invocations: true 34 | sort_pub_dependencies: false 35 | avoid_escaping_inner_quotes: false 36 | depend_on_referenced_packages: false 37 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/vizier/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.vizier 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/logo_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-hdpi/logo_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/logo_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-mdpi/logo_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/logo_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xhdpi/logo_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/logo_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xxhdpi/logo_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/logo_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/android/app/src/main/res/mipmap-xxxhdpi/logo_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #19103F 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 20 | #1B0E41 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/fastlane/Appfile: -------------------------------------------------------------------------------- 1 | json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one 2 | package_name("com.merixstudio.vizier") # e.g. com.krausefx.app 3 | -------------------------------------------------------------------------------- /android/fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | # For a list of all available plugins, check out 9 | # 10 | # https://docs.fastlane.tools/plugins/available-plugins 11 | # 12 | 13 | # Uncomment the line if you want fastlane to automatically update itself 14 | # update_fastlane 15 | 16 | default_platform(:android) 17 | 18 | platform :android do 19 | desc "Runs all the tests" 20 | lane :test do 21 | gradle(task: "test") 22 | end 23 | 24 | desc "Submit a new Beta Build to Crashlytics Beta" 25 | lane :beta do 26 | gradle(task: "clean assembleRelease") 27 | crashlytics 28 | 29 | # sh "your_script.sh" 30 | # You can also use other beta testing services here 31 | end 32 | 33 | desc "Deploy a new version to the Google Play" 34 | lane :deploy do 35 | gradle(task: "clean assembleRelease") 36 | upload_to_play_store 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/fonts/Ubuntu/Ubuntu-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/fonts/Ubuntu/Ubuntu-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Ubuntu/Ubuntu-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/fonts/Ubuntu/Ubuntu-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/Ubuntu/Ubuntu-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/fonts/Ubuntu/Ubuntu-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Ubuntu/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/fonts/Ubuntu/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /assets/mocks/finances/investments_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/finances/investments_image.png -------------------------------------------------------------------------------- /assets/mocks/finances/my_account_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/finances/my_account_image.png -------------------------------------------------------------------------------- /assets/mocks/portfolio/google_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/portfolio/google_logo.png -------------------------------------------------------------------------------- /assets/mocks/portfolio/netflix_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/portfolio/netflix_logo.png -------------------------------------------------------------------------------- /assets/mocks/portfolio/nike_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/portfolio/nike_logo.png -------------------------------------------------------------------------------- /assets/mocks/portfolio/tesla_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/portfolio/tesla_logo.png -------------------------------------------------------------------------------- /assets/mocks/transactions/apple_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/transactions/apple_logo.png -------------------------------------------------------------------------------- /assets/mocks/transactions/nike_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/transactions/nike_logo.png -------------------------------------------------------------------------------- /assets/mocks/transactions/paypal_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/transactions/paypal_logo.png -------------------------------------------------------------------------------- /assets/mocks/transactions/uber_eats_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/transactions/uber_eats_logo.png -------------------------------------------------------------------------------- /assets/mocks/transactions/vodafone_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/transactions/vodafone_logo.png -------------------------------------------------------------------------------- /assets/mocks/user/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/assets/mocks/user/avatar.png -------------------------------------------------------------------------------- /assets/svgs/card_face.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | any_map: true 7 | explicit_to_json: true 8 | field_rename: snake 9 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | gem "cocoapods" 5 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.0' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_ios_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_ios_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_secure_storage (3.3.1): 4 | - Flutter 5 | - shared_preferences_ios (0.0.1): 6 | - Flutter 7 | 8 | DEPENDENCIES: 9 | - Flutter (from `Flutter`) 10 | - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) 11 | - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) 12 | 13 | EXTERNAL SOURCES: 14 | Flutter: 15 | :path: Flutter 16 | flutter_secure_storage: 17 | :path: ".symlinks/plugins/flutter_secure_storage/ios" 18 | shared_preferences_ios: 19 | :path: ".symlinks/plugins/shared_preferences_ios/ios" 20 | 21 | SPEC CHECKSUMS: 22 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 23 | flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec 24 | shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad 25 | 26 | PODFILE CHECKSUM: 985e5b058f26709dc81f9ae74ea2b2775bdbcefe 27 | 28 | COCOAPODS: 1.11.3 29 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merixstudio/flutter-vizier-challenge/0cc01b5cc0795a9301a0fe73cc1b158f72fd8451/ios/Runner/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Vizier 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | vizier 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 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/fastlane/Appfile: -------------------------------------------------------------------------------- 1 | app_identifier("com.merixstudio.vizier") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple email address 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | -------------------------------------------------------------------------------- /ios/fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | # For a list of all available plugins, check out 9 | # 10 | # https://docs.fastlane.tools/plugins/available-plugins 11 | # 12 | 13 | # Uncomment the line if you want fastlane to automatically update itself 14 | # update_fastlane 15 | 16 | default_platform(:ios) 17 | 18 | platform :ios do 19 | desc "Description of what the lane does" 20 | lane :custom_lane do 21 | # add actions here: https://docs.fastlane.tools/actions 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/l10n 2 | template-arb-file: app_en.arb 3 | output-localization-file: app_localizations.dart -------------------------------------------------------------------------------- /lib/config/constants/animation_constants.dart: -------------------------------------------------------------------------------- 1 | part of 'app_constants.dart'; 2 | 3 | class _AnimationConstants { 4 | const _AnimationConstants(); 5 | 6 | Duration get defaultDuration => const Duration( 7 | milliseconds: 300, 8 | ); 9 | Duration get pieChartSweep => const Duration( 10 | seconds: 1, 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /lib/config/constants/api_constants.dart: -------------------------------------------------------------------------------- 1 | part of 'app_constants.dart'; 2 | 3 | class _ApiConstants { 4 | const _ApiConstants(); 5 | 6 | Duration get dummyLoadingDurationBuffor => Duration( 7 | seconds: DI.resolve().randomInt( 8 | maxNumber: 0, 9 | ), 10 | ); 11 | 12 | Duration get dummyLoadingDuration => Duration.zero; 13 | } 14 | -------------------------------------------------------------------------------- /lib/config/constants/app_constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/config/injector/di.dart'; 2 | import 'package:vizier/utils/dummy_util.dart'; 3 | 4 | part 'animation_constants.dart'; 5 | part 'api_constants.dart'; 6 | 7 | class AppConstants { 8 | const AppConstants._(); 9 | 10 | static const _AnimationConstants animation = _AnimationConstants(); 11 | static const _ApiConstants api = _ApiConstants(); 12 | } 13 | -------------------------------------------------------------------------------- /lib/config/formatters/card_date_input_formatter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class CardDateInputFormatter extends TextInputFormatter { 4 | @override 5 | TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { 6 | if (newValue.selection.baseOffset == 0) { 7 | return newValue; 8 | } 9 | final String text = newValue.text; 10 | final StringBuffer buffer = StringBuffer(); 11 | for (int i = 0; i < text.length; i++) { 12 | buffer.write(text[i]); 13 | final int nonZeroIndex = i + 1; 14 | if (nonZeroIndex.isEven && nonZeroIndex != text.length) { 15 | buffer.write('/'); 16 | } 17 | } 18 | final String newText = buffer.toString(); 19 | return newValue.copyWith( 20 | text: newText, 21 | selection: TextSelection.collapsed( 22 | offset: newText.length, 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/config/formatters/card_number_input_formatter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class CardNumberInputFormatter extends TextInputFormatter { 4 | @override 5 | TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { 6 | if (newValue.selection.baseOffset == 0) { 7 | return newValue; 8 | } 9 | final String text = newValue.text; 10 | final StringBuffer buffer = StringBuffer(); 11 | for (int i = 0; i < text.length; i++) { 12 | buffer.write(text[i]); 13 | final int nonZeroIndex = i + 1; 14 | if (nonZeroIndex % 4 == 0 && nonZeroIndex != text.length) { 15 | buffer.write(' '); 16 | } 17 | } 18 | final String newText = buffer.toString(); 19 | return newValue.copyWith( 20 | text: newText, 21 | selection: TextSelection.collapsed( 22 | offset: newText.length, 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/config/injector/di.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | import 'package:vizier/config/injector/di.config.dart'; 4 | 5 | @InjectableInit() 6 | class DI { 7 | DI._(); 8 | 9 | final GetIt _getIt = GetIt.I; 10 | 11 | static final DI instance = DI._(); 12 | 13 | Future setupInjection() async => $initGetIt(_getIt); 14 | 15 | // Use this to get registered instance 16 | static T resolve() => instance._getIt.get(); 17 | } 18 | -------------------------------------------------------------------------------- /lib/config/injector/module/common_module.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | @module 6 | abstract class CommonModule { 7 | @preResolve // if you need to pre resolve the value 8 | Future get prefs => SharedPreferences.getInstance(); 9 | 10 | @injectable 11 | FlutterSecureStorage get flutterSecureStorage => const FlutterSecureStorage(); 12 | } 13 | -------------------------------------------------------------------------------- /lib/config/styles/colors/app_colors_border.dart: -------------------------------------------------------------------------------- 1 | part of 'app_colors.dart'; 2 | 3 | class _Border { 4 | const _Border(); 5 | 6 | Color get success => AppColors.success100; 7 | Color get info => AppColors.info100; 8 | Color get error => AppColors.error300; 9 | } 10 | -------------------------------------------------------------------------------- /lib/config/styles/decorations/app_decorations.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/styles/colors/app_colors.dart'; 3 | import 'package:vizier/config/styles/dimensions/app_dimensions.dart'; 4 | import 'package:vizier/config/styles/text_styles/app_text_styles.dart'; 5 | 6 | part 'app_decorations_button.dart'; 7 | part 'app_decorations_input.dart'; 8 | 9 | class AppDecorations { 10 | const AppDecorations._(); 11 | 12 | static const _Button button = _Button(); 13 | static const _Input input = _Input(); 14 | 15 | static BoxDecoration defaultBorder() { 16 | return BoxDecoration( 17 | border: Border.all( 18 | color: AppColors.gray400, 19 | ), 20 | borderRadius: AppDimensions.radius.borderedRadius(), 21 | ); 22 | } 23 | 24 | static BoxDecoration navyBox() { 25 | return BoxDecoration( 26 | borderRadius: BorderRadius.circular(12.0), 27 | color: AppColors.navy, 28 | ); 29 | } 30 | 31 | static BoxDecoration primaryBox() { 32 | return navyBox().copyWith( 33 | color: AppColors.primary100, 34 | ); 35 | } 36 | 37 | static ShapeBorder get bottomSheetShape => const RoundedRectangleBorder( 38 | borderRadius: BorderRadius.vertical( 39 | top: Radius.circular(24.0), 40 | ), 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /lib/config/styles/decorations/app_decorations_button.dart: -------------------------------------------------------------------------------- 1 | part of 'app_decorations.dart'; 2 | 3 | class _Button { 4 | const _Button(); 5 | 6 | BorderRadius get _borderRadius => BorderRadius.circular(8.0); 7 | 8 | BoxDecoration primary({ 9 | bool isDisabled = false, 10 | }) { 11 | return BoxDecoration( 12 | borderRadius: _borderRadius, 13 | color: AppColors.primary100, 14 | ); 15 | } 16 | 17 | BoxDecoration secondary({ 18 | bool isDisabled = false, 19 | }) { 20 | return BoxDecoration( 21 | border: Border.all( 22 | color: AppColors.primary100, 23 | width: 2.0, 24 | ), 25 | borderRadius: _borderRadius, 26 | color: AppColors.white, 27 | ); 28 | } 29 | 30 | BoxDecoration disabled() { 31 | return BoxDecoration( 32 | borderRadius: _borderRadius, 33 | color: AppColors.gray500, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/config/styles/decorations/app_decorations_input.dart: -------------------------------------------------------------------------------- 1 | part of 'app_decorations.dart'; 2 | 3 | class _Input { 4 | const _Input(); 5 | 6 | InputDecoration standard({ 7 | required bool isValid, 8 | String? labelText, 9 | }) { 10 | return InputDecoration( 11 | border: _inputBorder( 12 | color: AppColors.gray400, 13 | ), 14 | contentPadding: AppDimensions.padding.defaultHorizontal(), 15 | disabledBorder: _inputBorder( 16 | color: AppColors.gray400, 17 | ), 18 | enabledBorder: _inputBorder( 19 | color: AppColors.gray400, 20 | ), 21 | errorBorder: _inputBorder( 22 | color: AppColors.error100, 23 | ), 24 | focusedErrorBorder: _inputBorder( 25 | color: AppColors.error100, 26 | ), 27 | errorStyle: AppTextStyles.caption2().copyWith( 28 | color: AppColors.error100, 29 | ), 30 | floatingLabelStyle: AppTextStyles.caption2().copyWith( 31 | color: isValid ? AppColors.navy : AppColors.error100, 32 | ), 33 | focusedBorder: _inputBorder( 34 | color: AppColors.primary100, 35 | ), 36 | hintStyle: AppTextStyles.text2().copyWith( 37 | color: AppColors.navy, 38 | ), 39 | labelText: labelText, 40 | labelStyle: AppTextStyles.text2().copyWith( 41 | color: AppColors.gray200, 42 | ), 43 | ); 44 | } 45 | } 46 | 47 | InputBorder _inputBorder({ 48 | required Color color, 49 | }) { 50 | return OutlineInputBorder( 51 | borderSide: BorderSide( 52 | color: color, 53 | ), 54 | borderRadius: AppDimensions.radius.defaultRadius(), 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /lib/config/styles/dimensions/app_dimensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | part 'app_dimensions_padding.dart'; 4 | part 'app_dimensions_radius.dart'; 5 | 6 | class AppDimensions { 7 | const AppDimensions._(); 8 | 9 | static const _Padding padding = _Padding(); 10 | static const _Radius radius = _Radius(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/config/styles/dimensions/app_dimensions_padding.dart: -------------------------------------------------------------------------------- 1 | part of 'app_dimensions.dart'; 2 | 3 | class _Padding { 4 | const _Padding(); 5 | 6 | double get defaultValue => 16.0; 7 | double get smallestValue => 4.0; 8 | double get smallValue => 8.0; 9 | double get bigValue => 24.0; 10 | double get biggerValue => 32.0; 11 | double get biggestValue => 48.0; 12 | 13 | EdgeInsets defaultHorizontal() { 14 | return EdgeInsets.symmetric( 15 | horizontal: defaultValue, 16 | ); 17 | } 18 | 19 | EdgeInsets smallHorizontal() { 20 | return EdgeInsets.symmetric( 21 | horizontal: smallValue, 22 | ); 23 | } 24 | 25 | EdgeInsets bigHorizontal() { 26 | return EdgeInsets.symmetric( 27 | horizontal: bigValue, 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/config/styles/dimensions/app_dimensions_radius.dart: -------------------------------------------------------------------------------- 1 | part of 'app_dimensions.dart'; 2 | 3 | class _Radius { 4 | const _Radius(); 5 | 6 | double get defaultValue => 8.0; 7 | double get borderedButton => 12.0; 8 | 9 | BorderRadius defaultRadius() { 10 | return BorderRadius.circular(defaultValue); 11 | } 12 | 13 | BorderRadius borderedRadius() { 14 | return BorderRadius.circular(borderedButton); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/config/styles/images/app_images.dart: -------------------------------------------------------------------------------- 1 | part 'svg_images.dart'; 2 | 3 | class AppImages { 4 | static const _SvgImages svg = _SvgImages(); 5 | } 6 | -------------------------------------------------------------------------------- /lib/config/styles/images/svg_images.dart: -------------------------------------------------------------------------------- 1 | part of 'app_images.dart'; 2 | 3 | class _SvgImages { 4 | const _SvgImages(); 5 | 6 | String get _path => 'assets/svgs'; 7 | String get manageMoney => '$_path/manage_money.svg'; 8 | String get allMoney => '$_path/all_money.svg'; 9 | String get chip => '$_path/chip.svg'; 10 | String get stayFocused => '$_path/stay_focused.svg'; 11 | String get moneyLogin => '$_path/money_login.svg'; 12 | String get banerBoy => '$_path/baner_boy.svg'; 13 | String get bitcoin => '$_path/bitcoin.svg'; 14 | String get cardFace => '$_path/card_face.svg'; 15 | String get mortgage => '$_path/mortgage.svg'; 16 | String get retirementGoals => '$_path/retirement_goals.svg'; 17 | String get mortgageDetails => '$_path/mortgage_details.svg'; 18 | String get retirementGoalsDetails => '$_path/retirement_goals_details.svg'; 19 | } 20 | -------------------------------------------------------------------------------- /lib/config/styles/text_styles/app_text_styles_button.dart: -------------------------------------------------------------------------------- 1 | part of 'app_text_styles.dart'; 2 | 3 | class _Button { 4 | const _Button(); 5 | 6 | TextStyle defaultStyle() { 7 | return AppTextStyles.defaultStyle().copyWith( 8 | color: AppColors.black, 9 | fontSize: 14.0, 10 | fontWeight: FontWeight.bold, 11 | letterSpacing: 0.5, 12 | ); 13 | } 14 | 15 | TextStyle primary({ 16 | bool isDisabled = false, 17 | }) { 18 | return defaultStyle().copyWith( 19 | color: AppColors.white, 20 | ); 21 | } 22 | 23 | TextStyle secondary({ 24 | bool isDisabled = false, 25 | }) { 26 | return defaultStyle().copyWith( 27 | color: AppColors.primary100, 28 | ); 29 | } 30 | 31 | TextStyle disabled() { 32 | return defaultStyle().copyWith( 33 | color: AppColors.gray200, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/config/styles/theme/app_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/styles/colors/app_colors.dart'; 3 | import 'package:vizier/config/styles/text_styles/app_text_styles.dart'; 4 | 5 | part 'dark_theme.dart'; 6 | part 'light_theme.dart'; 7 | 8 | enum ThemeType { light, dark } 9 | 10 | class AppTheme { 11 | final ThemeType type; 12 | 13 | AppTheme._(this.type); 14 | 15 | factory AppTheme.fromType(ThemeType type) => AppTheme._(type); 16 | 17 | ThemeData get themeData { 18 | switch (type) { 19 | case ThemeType.dark: 20 | return darkTheme; 21 | case ThemeType.light: 22 | default: 23 | return lightTheme; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/config/styles/theme/dark_theme.dart: -------------------------------------------------------------------------------- 1 | part of 'app_theme.dart'; 2 | 3 | ThemeData get darkTheme { 4 | return lightTheme; 5 | } 6 | -------------------------------------------------------------------------------- /lib/cubits/account_financial_breakdown/account_financial_breakdown_state.dart: -------------------------------------------------------------------------------- 1 | part of 'account_financial_breakdown_cubit.dart'; 2 | 3 | @freezed 4 | class AccountFinancialBreakdownState with _$AccountFinancialBreakdownState { 5 | const AccountFinancialBreakdownState._(); 6 | const factory AccountFinancialBreakdownState.initial() = _Initial; 7 | const factory AccountFinancialBreakdownState.loading() = _Loading; 8 | const factory AccountFinancialBreakdownState.loaded({ 9 | required DateTime from, 10 | required DateTime to, 11 | required List sections, 12 | required List transactionCategories, 13 | }) = _Loaded; 14 | const factory AccountFinancialBreakdownState.failure({ 15 | required AppError appError, 16 | }) = _Failure; 17 | 18 | List? get sections => mapOrNull( 19 | loaded: (state) => state.sections, 20 | ); 21 | 22 | List? get transactionCategories => mapOrNull( 23 | loaded: (state) => state.transactionCategories, 24 | ); 25 | 26 | DateTime? get from => mapOrNull( 27 | loaded: (state) => state.from, 28 | ); 29 | 30 | DateTime? get to => mapOrNull( 31 | loaded: (state) => state.to, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /lib/cubits/authentication/authentication_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/storages/token_storage.dart'; 5 | 6 | part 'authentication_state.dart'; 7 | part 'authentication_cubit.freezed.dart'; 8 | 9 | @injectable 10 | class AuthenticationCubit extends Cubit { 11 | final TokenStorage tokenStorage; 12 | 13 | AuthenticationCubit({ 14 | required this.tokenStorage, 15 | }) : super(const AuthenticationState.initial()); 16 | 17 | Future checkAuth() async { 18 | final bool hasToken = await tokenStorage.hasToken(); 19 | if (hasToken) { 20 | emit( 21 | const AuthenticationState.authenticated(), 22 | ); 23 | } else { 24 | emit( 25 | const AuthenticationState.unauthenticated(), 26 | ); 27 | } 28 | } 29 | 30 | Future authenticate({ 31 | required String accessToken, 32 | }) async { 33 | await tokenStorage.storeAccessToken(accessToken); 34 | emit( 35 | const AuthenticationState.authenticated(), 36 | ); 37 | } 38 | 39 | Future logout() async { 40 | await tokenStorage.clear(); 41 | emit( 42 | const AuthenticationState.logout(), 43 | ); 44 | emit( 45 | const AuthenticationState.unauthenticated(), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/cubits/authentication/authentication_state.dart: -------------------------------------------------------------------------------- 1 | part of 'authentication_cubit.dart'; 2 | 3 | @freezed 4 | class AuthenticationState with _$AuthenticationState { 5 | const AuthenticationState._(); 6 | const factory AuthenticationState.initial() = _Initial; 7 | const factory AuthenticationState.loading() = _Loading; 8 | const factory AuthenticationState.authenticated() = _Authenticated; 9 | const factory AuthenticationState.unauthenticated() = _Unauthenticated; 10 | const factory AuthenticationState.error(Error error) = _Error; 11 | const factory AuthenticationState.logout() = _Logout; 12 | 13 | bool get isLoading => this is _Loading; 14 | 15 | bool get isLogged => maybeWhen( 16 | authenticated: () => true, 17 | orElse: () => false, 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /lib/cubits/authentication/login/authentication_login_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/repositories/authentication/authentication_repository.dart'; 6 | import 'package:vizier/data/requests/authentication/authentication_request.dart'; 7 | import 'package:vizier/data/responses/authentication/authentication_response.dart'; 8 | import 'package:vizier/data/responses/response_status.dart'; 9 | 10 | part 'authentication_login_cubit.freezed.dart'; 11 | part 'authentication_login_state.dart'; 12 | 13 | @injectable 14 | class AuthenticationLoginCubit extends Cubit { 15 | final AuthenticationRepository authenticationRepository; 16 | 17 | AuthenticationLoginCubit({ 18 | required this.authenticationRepository, 19 | }) : super(const AuthenticationLoginState.initial()); 20 | 21 | Future login(AuthenticationRequest request) async { 22 | emit( 23 | const AuthenticationLoginState.loading(), 24 | ); 25 | 26 | final ResponseStatus responseStatus = await authenticationRepository.login(request); 27 | 28 | responseStatus.result( 29 | onSuccess: (response) { 30 | emit( 31 | AuthenticationLoginState.success( 32 | token: response.token, 33 | ), 34 | ); 35 | }, 36 | onError: (error) => emit( 37 | AuthenticationLoginState.error(error), 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/cubits/authentication/login/authentication_login_state.dart: -------------------------------------------------------------------------------- 1 | part of 'authentication_login_cubit.dart'; 2 | 3 | @freezed 4 | class AuthenticationLoginState with _$AuthenticationLoginState { 5 | const AuthenticationLoginState._(); 6 | const factory AuthenticationLoginState.initial() = _Initial; 7 | const factory AuthenticationLoginState.loading() = _Loading; 8 | const factory AuthenticationLoginState.success({ 9 | required String token, 10 | }) = _Success; 11 | const factory AuthenticationLoginState.error(AppError error) = _Error; 12 | 13 | bool get isLoading => maybeMap( 14 | loading: (_) => true, 15 | orElse: () => false, 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /lib/cubits/card_form/add/card_add_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/account/account_model.dart'; 6 | import 'package:vizier/data/repositories/wallet/wallet_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'card_add_state.dart'; 10 | part 'card_add_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class CardAddCubit extends Cubit { 14 | final WalletRepository walletRepository; 15 | 16 | CardAddCubit({ 17 | required this.walletRepository, 18 | }) : super(const CardAddState.initial()); 19 | 20 | Future add(AccountModel account) async { 21 | emit( 22 | const CardAddState.loading(), 23 | ); 24 | 25 | final ResponseStatus response = await walletRepository.addWalletAccount( 26 | account: account, 27 | ); 28 | response.result( 29 | onSuccess: (_) => emit( 30 | const CardAddState.success(), 31 | ), 32 | onError: (error) => emit( 33 | CardAddState.failure( 34 | error: error, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/cubits/card_form/add/card_add_state.dart: -------------------------------------------------------------------------------- 1 | part of 'card_add_cubit.dart'; 2 | 3 | @freezed 4 | class CardAddState with _$CardAddState { 5 | const CardAddState._(); 6 | 7 | const factory CardAddState.initial() = _Initial; 8 | const factory CardAddState.loading() = _Loading; 9 | const factory CardAddState.success() = _Success; 10 | const factory CardAddState.failure({ 11 | required AppError error, 12 | }) = _Error; 13 | 14 | bool get isLoading => maybeWhen( 15 | loading: () => true, 16 | orElse: () => false, 17 | ); 18 | 19 | bool get isSuccess => maybeWhen( 20 | success: () => true, 21 | orElse: () => false, 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /lib/cubits/card_form/card_form_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/models/account/account_model.dart'; 5 | import 'package:vizier/ui/models/card_account_type.dart'; 6 | 7 | part 'card_form_state.dart'; 8 | part 'card_form_cubit.freezed.dart'; 9 | 10 | @injectable 11 | class CardFormCubit extends Cubit { 12 | CardFormCubit() : super(CardFormState.initial()); 13 | 14 | void updateBilling(String billing) => emit( 15 | state.copyWith( 16 | billing: billing, 17 | ), 18 | ); 19 | 20 | void updateCvv(String cvv) => emit( 21 | state.copyWith( 22 | cvv: cvv, 23 | ), 24 | ); 25 | 26 | void updateExpiry(String expiry) => emit( 27 | state.copyWith( 28 | expiry: expiry, 29 | ), 30 | ); 31 | 32 | void updateName(String name) => emit( 33 | state.copyWith( 34 | name: name, 35 | ), 36 | ); 37 | 38 | void updateNumber(String number) => emit( 39 | state.copyWith( 40 | number: number, 41 | ), 42 | ); 43 | 44 | void updateType(CardAccountType type) => emit( 45 | state.copyWith( 46 | type: type, 47 | ), 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /lib/cubits/card_form/card_form_state.dart: -------------------------------------------------------------------------------- 1 | part of 'card_form_cubit.dart'; 2 | 3 | @freezed 4 | class CardFormState with _$CardFormState { 5 | const factory CardFormState({ 6 | required String billing, 7 | required String cvv, 8 | required String expiry, 9 | required String name, 10 | required String number, 11 | required CardAccountType type, 12 | }) = _CardFormState; 13 | 14 | const CardFormState._(); 15 | 16 | factory CardFormState.initial() => const CardFormState( 17 | billing: '', 18 | cvv: '', 19 | expiry: '', 20 | name: '', 21 | number: '', 22 | type: CardAccountType.front, 23 | ); 24 | 25 | AccountModel get account => AccountModel( 26 | balance: 0.0, 27 | change: 0.0, 28 | expiry: expiry, 29 | logo: '', 30 | name: name, 31 | number: number, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /lib/cubits/chart_tabs/chart_tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/l10n/app_loc.dart'; 3 | 4 | enum ChartTab { week, month, year, all } 5 | 6 | extension ChartTabExtension on ChartTab { 7 | String title(BuildContext context) { 8 | switch (this) { 9 | case ChartTab.week: 10 | return AppLoc.of(context).chartTabWeek; 11 | case ChartTab.month: 12 | return AppLoc.of(context).chartTabMonth; 13 | case ChartTab.year: 14 | return AppLoc.of(context).chartTabYear; 15 | case ChartTab.all: 16 | return AppLoc.of(context).chartTabAll; 17 | } 18 | } 19 | 20 | int days() { 21 | switch (this) { 22 | case ChartTab.week: 23 | return DateTime.daysPerWeek; 24 | case ChartTab.month: 25 | return DateTime.monthsPerYear; 26 | case ChartTab.year: 27 | return DateTime.monthsPerYear * 30; 28 | case ChartTab.all: 29 | return 0; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/cubits/chart_tabs/chart_tabs_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/cubits/chart_tabs/chart_tab.dart'; 5 | 6 | part 'chart_tabs_state.dart'; 7 | part 'chart_tabs_cubit.freezed.dart'; 8 | 9 | @injectable 10 | class ChartTabsCubit extends Cubit { 11 | ChartTabsCubit() 12 | : super( 13 | const ChartTabsState.tab(ChartTab.week), 14 | ); 15 | 16 | void changeTab(ChartTab tab) { 17 | emit( 18 | ChartTabsState.tab(tab), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/cubits/chart_tabs/chart_tabs_state.dart: -------------------------------------------------------------------------------- 1 | part of 'chart_tabs_cubit.dart'; 2 | 3 | @freezed 4 | class ChartTabsState with _$ChartTabsState { 5 | const factory ChartTabsState.tab(ChartTab selectedTab) = _Tab; 6 | } 7 | -------------------------------------------------------------------------------- /lib/cubits/company_listing/details/company_listing_details_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/company_asset/company_asset_model.dart'; 6 | import 'package:vizier/data/models/company_listing/details/company_listing_details_item_model.dart'; 7 | import 'package:vizier/data/repositories/portfolio/portfolio_repository.dart'; 8 | import 'package:vizier/data/responses/response_status.dart'; 9 | 10 | part 'company_listing_details_state.dart'; 11 | part 'company_listing_details_cubit.freezed.dart'; 12 | 13 | @injectable 14 | class CompanyListingDetailsCubit extends Cubit { 15 | final PortfolioRepository portfolioRepository; 16 | 17 | CompanyListingDetailsCubit({ 18 | required this.portfolioRepository, 19 | }) : super(const CompanyListingDetailsState.initial()); 20 | 21 | Future fetchData({ 22 | required CompanyAssetModel companyAsset, 23 | }) async { 24 | emit( 25 | const CompanyListingDetailsState.loading(), 26 | ); 27 | 28 | final ResponseStatus> response = await portfolioRepository.details(); 29 | response.result( 30 | onSuccess: (data) => emit( 31 | CompanyListingDetailsState.loaded( 32 | details: data, 33 | ), 34 | ), 35 | onError: (error) => emit( 36 | CompanyListingDetailsState.failure( 37 | error: error, 38 | ), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/cubits/company_listing/details/company_listing_details_state.dart: -------------------------------------------------------------------------------- 1 | part of 'company_listing_details_cubit.dart'; 2 | 3 | @freezed 4 | class CompanyListingDetailsState with _$CompanyListingDetailsState { 5 | const CompanyListingDetailsState._(); 6 | 7 | const factory CompanyListingDetailsState.initial() = _Initial; 8 | const factory CompanyListingDetailsState.loading() = _Loading; 9 | const factory CompanyListingDetailsState.loaded({ 10 | required List details, 11 | }) = _Loaded; 12 | const factory CompanyListingDetailsState.failure({ 13 | required AppError error, 14 | }) = _Error; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (_) => true, 18 | orElse: () => false, 19 | ); 20 | 21 | List get details => maybeWhen( 22 | loaded: (data) => data, 23 | orElse: () => [], 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/cubits/company_listing/history/company_listing_history_state.dart: -------------------------------------------------------------------------------- 1 | part of 'company_listing_history_cubit.dart'; 2 | 3 | @freezed 4 | class CompanyListingHistoryState with _$CompanyListingHistoryState { 5 | const CompanyListingHistoryState._(); 6 | 7 | const factory CompanyListingHistoryState.initial() = _Initial; 8 | const factory CompanyListingHistoryState.loading() = _Loading; 9 | const factory CompanyListingHistoryState.loaded({ 10 | required CompanyListingHistoryModel companyListingHistoryModel, 11 | }) = _Loaded; 12 | const factory CompanyListingHistoryState.failure({ 13 | required AppError error, 14 | }) = _Error; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (_) => true, 18 | orElse: () => false, 19 | ); 20 | 21 | CompanyListingHistoryModel? get companyListingHistoryModel => maybeMap( 22 | loaded: (state) => state.companyListingHistoryModel, 23 | orElse: () => null, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/cubits/financial_history/financial_history_state.dart: -------------------------------------------------------------------------------- 1 | part of 'financial_history_cubit.dart'; 2 | 3 | @freezed 4 | class FinancialHistoryState with _$FinancialHistoryState { 5 | const FinancialHistoryState._(); 6 | 7 | const factory FinancialHistoryState.initial() = _Initial; 8 | const factory FinancialHistoryState.loading() = _Loading; 9 | const factory FinancialHistoryState.loaded({ 10 | required FinancialHistoryModel financialHistory, 11 | }) = _Loaded; 12 | const factory FinancialHistoryState.failure({ 13 | required AppError error, 14 | }) = _Error; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (_) => true, 18 | orElse: () => false, 19 | ); 20 | 21 | FinancialHistoryModel? get financialHistory => maybeMap( 22 | loaded: (state) => state.financialHistory, 23 | orElse: () => null, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/cubits/goals/goals_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/goal/goal_model.dart'; 6 | import 'package:vizier/data/repositories/wallet/wallet_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'goals_state.dart'; 10 | part 'goals_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class GoalsCubit extends Cubit { 14 | final WalletRepository walletRepository; 15 | 16 | GoalsCubit({ 17 | required this.walletRepository, 18 | }) : super(const GoalsState.initial()); 19 | 20 | Future fetchData() async { 21 | emit( 22 | const GoalsState.loading(), 23 | ); 24 | 25 | final ResponseStatus> response = await walletRepository.goals(); 26 | response.result( 27 | onSuccess: (data) => emit( 28 | GoalsState.loaded( 29 | goals: data, 30 | ), 31 | ), 32 | onError: (error) => emit( 33 | GoalsState.failure( 34 | error: error, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/cubits/goals/goals_state.dart: -------------------------------------------------------------------------------- 1 | part of 'goals_cubit.dart'; 2 | 3 | @freezed 4 | class GoalsState with _$GoalsState { 5 | const factory GoalsState.initial() = _Initial; 6 | const factory GoalsState.loading() = _Loading; 7 | const factory GoalsState.loaded({ 8 | required List goals, 9 | }) = _Loaded; 10 | const factory GoalsState.failure({ 11 | required AppError error, 12 | }) = _Failure; 13 | } 14 | -------------------------------------------------------------------------------- /lib/cubits/my_finances/my_finances_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/finances_overview/finances_overview_model.dart'; 6 | import 'package:vizier/data/repositories/finances/finances_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'my_finances_state.dart'; 10 | part 'my_finances_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class MyFinancesCubit extends Cubit { 14 | final FinancesRepository financesRepository; 15 | 16 | MyFinancesCubit({ 17 | required this.financesRepository, 18 | }) : super(const MyFinancesState.initial()); 19 | 20 | Future fetchData() async { 21 | emit( 22 | const MyFinancesState.loading(), 23 | ); 24 | 25 | final ResponseStatus response = await financesRepository.overview(); 26 | response.result( 27 | onSuccess: (data) => emit( 28 | MyFinancesState.loaded( 29 | financesOverview: data, 30 | ), 31 | ), 32 | onError: (error) => emit( 33 | MyFinancesState.failure( 34 | error: error, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/cubits/my_finances/my_finances_state.dart: -------------------------------------------------------------------------------- 1 | part of 'my_finances_cubit.dart'; 2 | 3 | @freezed 4 | class MyFinancesState with _$MyFinancesState { 5 | const MyFinancesState._(); 6 | 7 | const factory MyFinancesState.initial() = _Initial; 8 | const factory MyFinancesState.loading() = _Loading; 9 | const factory MyFinancesState.loaded({ 10 | required FinancesOverviewModel financesOverview, 11 | }) = _Loaded; 12 | const factory MyFinancesState.failure({ 13 | required AppError error, 14 | }) = _Error; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (_) => true, 18 | orElse: () => false, 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /lib/cubits/my_portfolio/my_portfolio_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/portfolio_overview/portfolio_overview_model.dart'; 6 | import 'package:vizier/data/repositories/portfolio/portfolio_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'my_portfolio_state.dart'; 10 | part 'my_portfolio_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class MyPortfolioCubit extends Cubit { 14 | final PortfolioRepository portfolioRepository; 15 | 16 | MyPortfolioCubit({ 17 | required this.portfolioRepository, 18 | }) : super(const MyPortfolioState.initial()); 19 | 20 | Future fetchData() async { 21 | emit( 22 | const MyPortfolioState.loading(), 23 | ); 24 | 25 | final ResponseStatus response = await portfolioRepository.overview(); 26 | response.result( 27 | onSuccess: (data) => emit( 28 | MyPortfolioState.loaded( 29 | portfolioOverview: data, 30 | ), 31 | ), 32 | onError: (error) => emit( 33 | MyPortfolioState.failure( 34 | error: error, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/cubits/my_portfolio/my_portfolio_state.dart: -------------------------------------------------------------------------------- 1 | part of 'my_portfolio_cubit.dart'; 2 | 3 | @freezed 4 | class MyPortfolioState with _$MyPortfolioState { 5 | const MyPortfolioState._(); 6 | 7 | const factory MyPortfolioState.initial() = _Initial; 8 | const factory MyPortfolioState.loading() = _Loading; 9 | const factory MyPortfolioState.loaded({ 10 | required PortfolioOverviewModel portfolioOverview, 11 | }) = _Loaded; 12 | const factory MyPortfolioState.failure({ 13 | required AppError error, 14 | }) = _Error; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (_) => true, 18 | orElse: () => false, 19 | ); 20 | 21 | PortfolioOverviewModel? get portfolioOverview => maybeMap( 22 | loaded: (state) => state.portfolioOverview, 23 | orElse: () => null, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/cubits/offers/details/offer_details_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | import 'package:injectable/injectable.dart'; 6 | import 'package:vizier/data/models/offer/offer_model.dart'; 7 | import 'package:vizier/data/models/offer/slider_item/offer_slider_model.dart'; 8 | 9 | part 'offer_details_state.dart'; 10 | part 'offer_details_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class OfferDetailsCubit extends Cubit { 14 | OfferDetailsCubit() : super(OfferDetailsState.initial()); 15 | 16 | void initial(OfferModel offer) => emit( 17 | state.copyWith( 18 | expectedValue: offer.expectedValue, 19 | sliders: offer.sliders, 20 | ), 21 | ); 22 | 23 | void updateSlider(OfferSliderModel slider, double value) { 24 | emit( 25 | state.copyWith( 26 | sliders: state.sliders 27 | .map( 28 | (e) => e == slider 29 | ? e.copyWith( 30 | current: value, 31 | ) 32 | : e, 33 | ) 34 | .toList(), 35 | ), 36 | ); 37 | 38 | emit( 39 | state.copyWith( 40 | calculatedValue: _calculateValue(), 41 | ), 42 | ); 43 | } 44 | 45 | double _calculateValue() { 46 | //TODO (MT): Change logic 47 | final double leftToGoal = max((state.expectedValue ?? 0.0) - state.sliders.last.actualValue, 0); 48 | final double estPerMonth = state.sliders.first.actualValue * 1.3; 49 | return estPerMonth + leftToGoal / 300; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/cubits/offers/details/offer_details_state.dart: -------------------------------------------------------------------------------- 1 | part of 'offer_details_cubit.dart'; 2 | 3 | @freezed 4 | class OfferDetailsState with _$OfferDetailsState { 5 | const factory OfferDetailsState({ 6 | required List sliders, 7 | double? expectedValue, 8 | double? calculatedValue, 9 | }) = _OfferDetailsState; 10 | 11 | factory OfferDetailsState.initial() => const OfferDetailsState( 12 | sliders: [], 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /lib/cubits/offers/list/offer_list_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/offer/offer_model.dart'; 6 | import 'package:vizier/data/repositories/offers/offers_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'offer_list_state.dart'; 10 | part 'offer_list_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class OfferListCubit extends Cubit { 14 | final OffersRepository offersRepository; 15 | 16 | OfferListCubit({ 17 | required this.offersRepository, 18 | }) : super(const OfferListState.loading()); 19 | 20 | Future fetchData() async { 21 | emit( 22 | const OfferListState.loading(), 23 | ); 24 | 25 | final ResponseStatus> response = await offersRepository.all(); 26 | response.result( 27 | onSuccess: (data) => emit( 28 | OfferListState.loaded( 29 | offers: data, 30 | ), 31 | ), 32 | onError: (error) => emit( 33 | OfferListState.failure( 34 | error: error, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/cubits/offers/list/offer_list_state.dart: -------------------------------------------------------------------------------- 1 | part of 'offer_list_cubit.dart'; 2 | 3 | @freezed 4 | class OfferListState with _$OfferListState { 5 | const OfferListState._(); 6 | 7 | const factory OfferListState.loaded({ 8 | required List offers, 9 | }) = _Loaded; 10 | const factory OfferListState.loading() = _Loading; 11 | const factory OfferListState.failure({ 12 | required AppError error, 13 | }) = _Error; 14 | } 15 | -------------------------------------------------------------------------------- /lib/cubits/portfolio_watchlist/portfolio_watchlist_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/company_asset/company_asset_model.dart'; 6 | import 'package:vizier/data/repositories/portfolio/portfolio_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'portfolio_watchlist_state.dart'; 10 | part 'portfolio_watchlist_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class PortfolioWatchlistCubit extends Cubit { 14 | final PortfolioRepository portfolioRepository; 15 | 16 | PortfolioWatchlistCubit({ 17 | required this.portfolioRepository, 18 | }) : super(const PortfolioWatchlistState.initial()); 19 | 20 | Future fetchData() async { 21 | emit( 22 | const PortfolioWatchlistState.loading(), 23 | ); 24 | 25 | final ResponseStatus> response = await portfolioRepository.watchlist(); 26 | response.result( 27 | onSuccess: (data) => emit( 28 | PortfolioWatchlistState.loaded( 29 | companyAssets: data, 30 | ), 31 | ), 32 | onError: (error) => emit( 33 | PortfolioWatchlistState.failure( 34 | error: error, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/cubits/portfolio_watchlist/portfolio_watchlist_state.dart: -------------------------------------------------------------------------------- 1 | part of 'portfolio_watchlist_cubit.dart'; 2 | 3 | @freezed 4 | class PortfolioWatchlistState with _$PortfolioWatchlistState { 5 | const PortfolioWatchlistState._(); 6 | 7 | const factory PortfolioWatchlistState.initial() = _Initial; 8 | const factory PortfolioWatchlistState.loading() = _Loading; 9 | const factory PortfolioWatchlistState.loaded({ 10 | required List companyAssets, 11 | }) = _Loaded; 12 | const factory PortfolioWatchlistState.failure({ 13 | required AppError error, 14 | }) = _Error; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (_) => true, 18 | orElse: () => false, 19 | ); 20 | 21 | List? get companyAssets => maybeMap( 22 | loaded: (state) => state.companyAssets, 23 | orElse: () => null, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/cubits/retirement_plan/retirement_plan_state.dart: -------------------------------------------------------------------------------- 1 | part of 'retirement_plan_cubit.dart'; 2 | 3 | @freezed 4 | class RetirementPlanState with _$RetirementPlanState { 5 | const RetirementPlanState._(); 6 | 7 | const factory RetirementPlanState.initial() = _Initial; 8 | const factory RetirementPlanState.loading() = _Loading; 9 | const factory RetirementPlanState.loaded({ 10 | required RetirementPlanModel retirementPlan, 11 | }) = _Loaded; 12 | const factory RetirementPlanState.failure({ 13 | required AppError error, 14 | }) = _Error; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (_) => true, 18 | orElse: () => false, 19 | ); 20 | 21 | RetirementPlanModel? get retirementPlan => maybeMap( 22 | loaded: (state) => state.retirementPlan, 23 | orElse: () => null, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/cubits/transaction_history/transaction_history_state.dart: -------------------------------------------------------------------------------- 1 | part of 'transaction_history_cubit.dart'; 2 | 3 | @freezed 4 | class TransactionHistoryState with _$TransactionHistoryState { 5 | const TransactionHistoryState._(); 6 | 7 | const factory TransactionHistoryState.initial() = _Initial; 8 | const factory TransactionHistoryState.loading() = _Loading; 9 | const factory TransactionHistoryState.loaded({ 10 | required List transactions, 11 | }) = _Loaded; 12 | const factory TransactionHistoryState.failure({ 13 | required AppError error, 14 | }) = _Failure; 15 | 16 | List? get transactions => mapOrNull( 17 | loaded: (state) => state.transactions, 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /lib/cubits/user/notifications/user_notifications_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/repositories/user/user_repository.dart'; 6 | import 'package:vizier/data/responses/response_status.dart'; 7 | import 'package:vizier/data/responses/user/notifications/user_notifications_response.dart'; 8 | 9 | part 'user_notifications_state.dart'; 10 | part 'user_notifications_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class UserNotificationsCubit extends Cubit { 14 | final UserRepository userRepository; 15 | 16 | UserNotificationsCubit({ 17 | required this.userRepository, 18 | }) : super(const UserNotificationsState.initial()); 19 | 20 | Future fetchData() async { 21 | emit( 22 | const UserNotificationsState.loading(), 23 | ); 24 | final ResponseStatus response = await userRepository.notifications(); 25 | response.result( 26 | onSuccess: (data) => emit( 27 | UserNotificationsState.loaded( 28 | areNotificationsEnabled: data.areNotificationsEnabled, 29 | ), 30 | ), 31 | onError: (error) => emit( 32 | UserNotificationsState.failure( 33 | appError: error, 34 | ), 35 | ), 36 | ); 37 | } 38 | 39 | Future update({ 40 | required bool areNotificationsEnabled, 41 | }) async { 42 | emit( 43 | UserNotificationsState.loaded( 44 | areNotificationsEnabled: areNotificationsEnabled, 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/cubits/user/notifications/user_notifications_state.dart: -------------------------------------------------------------------------------- 1 | part of 'user_notifications_cubit.dart'; 2 | 3 | @freezed 4 | class UserNotificationsState with _$UserNotificationsState { 5 | const UserNotificationsState._(); 6 | 7 | const factory UserNotificationsState.initial() = _Initial; 8 | const factory UserNotificationsState.loading() = _Loading; 9 | const factory UserNotificationsState.loaded({ 10 | required bool areNotificationsEnabled, 11 | }) = _Loaded; 12 | const factory UserNotificationsState.failure({ 13 | required AppError appError, 14 | }) = _Failure; 15 | 16 | bool? get areNotificationsEnabled => maybeMap( 17 | loaded: (state) => state.areNotificationsEnabled, 18 | orElse: () => null, 19 | ); 20 | 21 | bool get isLoading => maybeMap( 22 | loading: (_) => true, 23 | orElse: () => false, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/cubits/user/user_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/user/user_model.dart'; 6 | import 'package:vizier/data/repositories/user/user_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'user_state.dart'; 10 | part 'user_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class UserCubit extends Cubit { 14 | final UserRepository userRepository; 15 | 16 | UserCubit({ 17 | required this.userRepository, 18 | }) : super(const UserState.initial()); 19 | 20 | Future fetchMe() async { 21 | _emitLoading(); 22 | final ResponseStatus responseStatus = await userRepository.me(); 23 | responseStatus.result( 24 | onSuccess: (response) { 25 | emit( 26 | UserState.loaded(response), 27 | ); 28 | }, 29 | onError: (error) => _emitError(error), 30 | ); 31 | } 32 | 33 | void logout() { 34 | emit( 35 | const UserState.initial(), 36 | ); 37 | } 38 | 39 | void _emitLoading() { 40 | if (state.user != null) { 41 | emit( 42 | UserState.upating(state.user!), 43 | ); 44 | } else { 45 | emit( 46 | const UserState.loading(), 47 | ); 48 | } 49 | } 50 | 51 | void _emitError(AppError appError) { 52 | if (state.user != null) { 53 | emit( 54 | UserState.errorWithData( 55 | userModel: state.user!, 56 | appError: appError, 57 | ), 58 | ); 59 | } else { 60 | emit( 61 | UserState.error(appError), 62 | ); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/cubits/user/user_state.dart: -------------------------------------------------------------------------------- 1 | part of 'user_cubit.dart'; 2 | 3 | @freezed 4 | class UserState with _$UserState { 5 | const UserState._(); 6 | const factory UserState.initial() = _Initial; 7 | const factory UserState.loading() = _Loading; 8 | const factory UserState.loaded(UserModel userModel) = _Loaded; 9 | const factory UserState.upating(UserModel userModel) = _Updating; 10 | const factory UserState.errorWithData({ 11 | required UserModel userModel, 12 | required AppError appError, 13 | }) = _LoadedWithData; 14 | const factory UserState.error(AppError appError) = _Error; 15 | 16 | UserModel? get user => maybeMap( 17 | loaded: (state) => state.userModel, 18 | upating: (state) => state.userModel, 19 | errorWithData: (state) => state.userModel, 20 | orElse: () => null, 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /lib/cubits/wallet/wallet_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/errors/app_error.dart'; 5 | import 'package:vizier/data/models/wallet/wallet_model.dart'; 6 | import 'package:vizier/data/repositories/wallet/wallet_repository.dart'; 7 | import 'package:vizier/data/responses/response_status.dart'; 8 | 9 | part 'wallet_state.dart'; 10 | part 'wallet_cubit.freezed.dart'; 11 | 12 | @injectable 13 | class WalletCubit extends Cubit { 14 | final WalletRepository walletRepository; 15 | 16 | WalletCubit({ 17 | required this.walletRepository, 18 | }) : super(const WalletState.initial()); 19 | 20 | Future fetchData() async { 21 | emit( 22 | const WalletState.loading(), 23 | ); 24 | 25 | final ResponseStatus response = await walletRepository.wallet(); 26 | response.result( 27 | onSuccess: (data) => emit( 28 | WalletState.loaded( 29 | wallet: data, 30 | ), 31 | ), 32 | onError: (error) => emit( 33 | WalletState.failure( 34 | error: error, 35 | ), 36 | ), 37 | ); 38 | } 39 | 40 | void logout() { 41 | emit( 42 | const WalletState.initial(), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/cubits/wallet/wallet_state.dart: -------------------------------------------------------------------------------- 1 | part of 'wallet_cubit.dart'; 2 | 3 | @freezed 4 | class WalletState with _$WalletState { 5 | const WalletState._(); 6 | 7 | const factory WalletState.initial() = _Initial; 8 | const factory WalletState.loading() = _Loading; 9 | const factory WalletState.loaded({ 10 | required WalletModel wallet, 11 | }) = _Loaded; 12 | const factory WalletState.failure({ 13 | required AppError error, 14 | }) = _Failure; 15 | 16 | bool get isLoading => maybeMap( 17 | loading: (state) => true, 18 | orElse: () => false, 19 | ); 20 | 21 | WalletModel? get wallet => mapOrNull( 22 | loaded: (state) => state.wallet, 23 | ); 24 | 25 | double? get assetsChange => mapOrNull( 26 | loaded: (state) => state.wallet.accounts.map((e) => e.change).reduce( 27 | (value, element) => value + element, 28 | ), 29 | ); 30 | 31 | double? get assetsPercentageChange => mapOrNull( 32 | loaded: (state) => state.assetsChange != null ? (state.assetsChange! / state.wallet.balance * 100) : 0.0, 33 | ); 34 | 35 | int? get goalProgress => mapOrNull( 36 | loaded: (state) => (state.wallet.balance / state.wallet.goal * 100).ceil(), 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /lib/data/data_sources/authentication/authentication_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/requests/authentication/authentication_request.dart'; 2 | import 'package:vizier/data/responses/authentication/authentication_response.dart'; 3 | import 'package:vizier/data/responses/response_status.dart'; 4 | 5 | abstract class AuthenticationDataSource { 6 | Future> getLogin(AuthenticationRequest request); 7 | } 8 | -------------------------------------------------------------------------------- /lib/data/data_sources/authentication/dummy_authentication_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/config/constants/app_constants.dart'; 3 | import 'package:vizier/data/data_sources/authentication/authentication_data_source.dart'; 4 | import 'package:vizier/data/requests/authentication/authentication_request.dart'; 5 | import 'package:vizier/data/responses/authentication/authentication_response.dart'; 6 | import 'package:vizier/data/responses/response_status.dart'; 7 | import 'package:vizier/utils/dummy_util.dart'; 8 | 9 | @Injectable(as: AuthenticationDataSource) 10 | class DummyAuthenticationDataSource extends AuthenticationDataSource { 11 | final DummyUtil dummyUtil; 12 | 13 | DummyAuthenticationDataSource({ 14 | required this.dummyUtil, 15 | }); 16 | 17 | @override 18 | Future> getLogin( 19 | AuthenticationRequest request, 20 | ) async { 21 | await Future.delayed(AppConstants.api.dummyLoadingDuration); 22 | return ResponseStatus.success( 23 | AuthenticationResponse( 24 | token: dummyUtil.randomString(), 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/data/data_sources/finances/finances_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/finances_overview/finances_overview_model.dart'; 2 | import 'package:vizier/data/models/financial_history/financial_history_model.dart'; 3 | import 'package:vizier/data/models/retirement_plan/retirement_plan_model.dart'; 4 | import 'package:vizier/data/responses/response_status.dart'; 5 | 6 | abstract class FinancesDataSource { 7 | Future> overview(); 8 | Future> history({ 9 | required int daysBack, 10 | }); 11 | Future> retirementPlan({ 12 | required int daysTo, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /lib/data/data_sources/offers/dummy_offers_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/data/data_sources/offers/offers_data_source.dart'; 3 | import 'package:vizier/data/models/offer/offer_model.dart'; 4 | import 'package:vizier/data/responses/response_status.dart'; 5 | import 'package:vizier/mocks/mock_factory.dart'; 6 | 7 | @Injectable(as: OffersDataSource) 8 | class DummyOffersDataSource extends OffersDataSource { 9 | final MockFactory mockFactory; 10 | 11 | DummyOffersDataSource({ 12 | required this.mockFactory, 13 | }); 14 | 15 | @override 16 | Future>> all() async { 17 | return ResponseStatus.success( 18 | mockFactory.offers.prepareAll(), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/data_sources/offers/offers_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/offer/offer_model.dart'; 2 | import 'package:vizier/data/responses/response_status.dart'; 3 | 4 | abstract class OffersDataSource { 5 | Future>> all(); 6 | } 7 | -------------------------------------------------------------------------------- /lib/data/data_sources/portfolio/portfolio_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/company_asset/company_asset_model.dart'; 2 | import 'package:vizier/data/models/company_listing/details/company_listing_details_item_model.dart'; 3 | import 'package:vizier/data/models/company_listing/history/company_listing_history_model.dart'; 4 | import 'package:vizier/data/models/portfolio_overview/portfolio_overview_model.dart'; 5 | import 'package:vizier/data/responses/response_status.dart'; 6 | 7 | abstract class PortfolioDataSource { 8 | Future>> watchlist(); 9 | Future> overview(); 10 | Future>> details(); 11 | Future> history({ 12 | required CompanyAssetModel companyAsset, 13 | required int daysBack, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /lib/data/data_sources/user/dummy_user_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/config/constants/app_constants.dart'; 3 | import 'package:vizier/data/data_sources/user/user_data_source.dart'; 4 | import 'package:vizier/data/models/user/user_model.dart'; 5 | import 'package:vizier/data/responses/response_status.dart'; 6 | import 'package:vizier/data/responses/user/notifications/user_notifications_response.dart'; 7 | import 'package:vizier/mocks/mock_factory.dart'; 8 | 9 | @Injectable(as: UserDataSource) 10 | class DummyUserDataSource extends UserDataSource { 11 | final MockFactory mockFactory; 12 | 13 | DummyUserDataSource({ 14 | required this.mockFactory, 15 | }); 16 | 17 | @override 18 | Future> me() async { 19 | await Future.delayed(AppConstants.api.dummyLoadingDuration); 20 | 21 | return ResponseStatus.success( 22 | mockFactory.user.prepareUser(), 23 | ); 24 | } 25 | 26 | @override 27 | Future> notifications() async { 28 | await Future.delayed(AppConstants.api.dummyLoadingDuration); 29 | 30 | return ResponseStatus.success( 31 | mockFactory.user.prepareUserNotificationsResponse(), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/data/data_sources/user/user_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/user/user_model.dart'; 2 | import 'package:vizier/data/responses/response_status.dart'; 3 | import 'package:vizier/data/responses/user/notifications/user_notifications_response.dart'; 4 | 5 | abstract class UserDataSource { 6 | Future> me(); 7 | Future> notifications(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/data/data_sources/wallet/wallet_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/account/account_model.dart'; 2 | import 'package:vizier/data/models/account/breakdown/account_breakdown_model.dart'; 3 | import 'package:vizier/data/models/goal/goal_model.dart'; 4 | import 'package:vizier/data/models/transaction/transaction_model.dart'; 5 | import 'package:vizier/data/models/wallet/wallet_model.dart'; 6 | import 'package:vizier/data/responses/response_status.dart'; 7 | 8 | abstract class WalletDataSource { 9 | Future> addWalletAccount({ 10 | required AccountModel account, 11 | }); 12 | Future> accountBreakdown({ 13 | required int daysBack, 14 | required AccountModel account, 15 | }); 16 | Future>> goals(); 17 | Future> wallet(); 18 | Future>> transactionHistory({ 19 | required AccountModel accountModel, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/errors/app_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:vizier/l10n/app_loc.dart'; 4 | part 'app_error.freezed.dart'; 5 | 6 | @freezed 7 | class AppError with _$AppError implements Exception { 8 | AppError._(); 9 | 10 | factory AppError.mockFailure() = _MockFailure; 11 | factory AppError.networkFailure() = _NetworkError; 12 | factory AppError.serverFailure({ 13 | required int? statusCode, 14 | required Map? json, 15 | }) = _ServerError; 16 | factory AppError.unknownFailure(dynamic error) = _UnknownError; 17 | 18 | @override 19 | String toString() { 20 | return 'Use localizedTitle or localizedContent methods instead of toString()'; 21 | } 22 | 23 | String localizedTitle(BuildContext context) { 24 | return when( 25 | mockFailure: () => AppLoc.of(context).appErrorMockTitle, 26 | networkFailure: () => AppLoc.of(context).appErrorNetworkTitle, 27 | serverFailure: (_, __) => AppLoc.of(context).appErrorServerTitle, 28 | unknownFailure: (_) => AppLoc.of(context).appErrorUnknownTitle, 29 | ); 30 | } 31 | 32 | String localizedMessage(BuildContext context) { 33 | return when( 34 | mockFailure: () => AppLoc.of(context).appErrorMockMessage, 35 | networkFailure: () => AppLoc.of(context).appErrorNetworkMessage, 36 | serverFailure: (statusCode, json) { 37 | return AppLoc.of(context).appErrorServerMessage(statusCode.toString(), json.toString()); 38 | }, 39 | unknownFailure: (error) => error.toString(), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/data/models/account/account_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'account_model.g.dart'; 4 | part 'account_model.freezed.dart'; 5 | 6 | @freezed 7 | class AccountModel with _$AccountModel { 8 | const factory AccountModel({ 9 | required String logo, 10 | required String name, 11 | required String number, 12 | required double balance, 13 | required double change, 14 | required String expiry, 15 | }) = _AccountModel; 16 | 17 | const AccountModel._(); 18 | 19 | factory AccountModel.fromJson(Map json) => _$AccountModelFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/models/account/breakdown/account_breakdown_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/data/models/account/account_model.dart'; 3 | import 'package:vizier/data/models/transaction/category/transaction_category_model.dart'; 4 | 5 | part 'account_breakdown_model.g.dart'; 6 | part 'account_breakdown_model.freezed.dart'; 7 | 8 | @freezed 9 | class AccountBreakdownModel with _$AccountBreakdownModel { 10 | const factory AccountBreakdownModel({ 11 | required DateTime from, 12 | required DateTime to, 13 | required List transactionCategories, 14 | required AccountModel account, 15 | }) = _AccountBreakdownModel; 16 | 17 | const AccountBreakdownModel._(); 18 | 19 | factory AccountBreakdownModel.fromJson(Map json) => _$AccountBreakdownModelFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/models/company_asset/company_asset_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/extensions/extensions.dart'; 3 | 4 | part 'company_asset_model.g.dart'; 5 | part 'company_asset_model.freezed.dart'; 6 | 7 | @freezed 8 | class CompanyAssetModel with _$CompanyAssetModel { 9 | const factory CompanyAssetModel({ 10 | required String shortName, 11 | required String name, 12 | required String logo, 13 | required double currentValue, 14 | required double changePercentage, 15 | }) = _CompanyAssetModel; 16 | 17 | const CompanyAssetModel._(); 18 | 19 | factory CompanyAssetModel.fromJson(Map json) => _$CompanyAssetModelFromJson(json); 20 | 21 | String get fullName => [shortName, name].where((element) => element.isNotBlank).join(' '); 22 | } 23 | -------------------------------------------------------------------------------- /lib/data/models/company_listing/details/company_listing_details_item_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'company_listing_details_item_model.g.dart'; 4 | part 'company_listing_details_item_model.freezed.dart'; 5 | 6 | @freezed 7 | class CompanyListingDetailsItemModel with _$CompanyListingDetailsItemModel { 8 | const factory CompanyListingDetailsItemModel({ 9 | required String description, 10 | required String index, 11 | required String title, 12 | }) = _CompanyListingDetailsItemModel; 13 | 14 | const CompanyListingDetailsItemModel._(); 15 | 16 | factory CompanyListingDetailsItemModel.fromJson(Map json) => 17 | _$CompanyListingDetailsItemModelFromJson(json); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/models/company_listing/history/company_listing_history_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/data/models/company_listing/history/data/company_listing_history_data_model.dart'; 3 | 4 | part 'company_listing_history_model.g.dart'; 5 | part 'company_listing_history_model.freezed.dart'; 6 | 7 | @freezed 8 | class CompanyListingHistoryModel with _$CompanyListingHistoryModel { 9 | const factory CompanyListingHistoryModel({ 10 | required List history, 11 | required DateTime from, 12 | required DateTime to, 13 | required int daysBack, 14 | }) = _CompanyListingHistoryModel; 15 | 16 | const CompanyListingHistoryModel._(); 17 | 18 | factory CompanyListingHistoryModel.fromJson(Map json) => _$CompanyListingHistoryModelFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/models/company_listing/history/data/company_listing_history_data_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:vizier/data/models/company_listing/history/value/company_listing_history_value_model.dart'; 5 | 6 | part 'company_listing_history_data_model.g.dart'; 7 | part 'company_listing_history_data_model.freezed.dart'; 8 | 9 | @freezed 10 | class CompanyListingHistoryDataModel with _$CompanyListingHistoryDataModel { 11 | const factory CompanyListingHistoryDataModel({ 12 | required DateTime date, 13 | required bool isProfit, 14 | required CompanyListingHistoryValueModel value1, 15 | required CompanyListingHistoryValueModel value2, 16 | }) = _CompanyListingHistoryDataModel; 17 | 18 | double get maxValues => max(max(value1.min, value1.max), max(value2.min, value2.max)); 19 | 20 | double get minValues => min(min(value1.min, value2.max), min(value2.min, value2.max)); 21 | 22 | const CompanyListingHistoryDataModel._(); 23 | 24 | factory CompanyListingHistoryDataModel.fromJson(Map json) => 25 | _$CompanyListingHistoryDataModelFromJson(json); 26 | } 27 | -------------------------------------------------------------------------------- /lib/data/models/company_listing/history/value/company_listing_history_value_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'company_listing_history_value_model.g.dart'; 4 | part 'company_listing_history_value_model.freezed.dart'; 5 | 6 | @freezed 7 | class CompanyListingHistoryValueModel with _$CompanyListingHistoryValueModel { 8 | const factory CompanyListingHistoryValueModel({ 9 | required double min, 10 | required double max, 11 | }) = _CompanyListingHistoryValueModel; 12 | 13 | const CompanyListingHistoryValueModel._(); 14 | 15 | factory CompanyListingHistoryValueModel.fromJson(Map json) => 16 | _$CompanyListingHistoryValueModelFromJson(json); 17 | } 18 | -------------------------------------------------------------------------------- /lib/data/models/finances_overview/finances_overview_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'finances_overview_model.g.dart'; 4 | part 'finances_overview_model.freezed.dart'; 5 | 6 | @freezed 7 | class FinancesOverviewModel with _$FinancesOverviewModel { 8 | const factory FinancesOverviewModel({ 9 | required double cashBalance, 10 | required double spent, 11 | required DateTime since, 12 | }) = _FinancesOverviewModel; 13 | 14 | const FinancesOverviewModel._(); 15 | 16 | factory FinancesOverviewModel.fromJson(Map json) => _$FinancesOverviewModelFromJson(json); 17 | } 18 | -------------------------------------------------------------------------------- /lib/data/models/financial_history/financial_history_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/data/models/financial_history/history_data/financial_history_data_model.dart'; 3 | 4 | part 'financial_history_model.g.dart'; 5 | part 'financial_history_model.freezed.dart'; 6 | 7 | @freezed 8 | class FinancialHistoryModel with _$FinancialHistoryModel { 9 | const factory FinancialHistoryModel({ 10 | required List history, 11 | required DateTime from, 12 | required DateTime to, 13 | required int daysBack, 14 | }) = _FinancialHistoryModel; 15 | 16 | const FinancialHistoryModel._(); 17 | 18 | factory FinancialHistoryModel.fromJson(Map json) => _$FinancialHistoryModelFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/models/financial_history/history_data/financial_history_data_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'financial_history_data_model.g.dart'; 6 | part 'financial_history_data_model.freezed.dart'; 7 | 8 | @freezed 9 | class FinancialHistoryDataModel with _$FinancialHistoryDataModel { 10 | const factory FinancialHistoryDataModel({ 11 | required DateTime date, 12 | required double balance, 13 | required double spent, 14 | }) = _FinancialHistoryDataModel; 15 | 16 | const FinancialHistoryDataModel._(); 17 | 18 | double get maxBalanceOrSpent => max(balance, spent); 19 | 20 | factory FinancialHistoryDataModel.fromJson(Map json) => _$FinancialHistoryDataModelFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/models/goal/goal_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'goal_model.g.dart'; 4 | part 'goal_model.freezed.dart'; 5 | 6 | @freezed 7 | class GoalModel with _$GoalModel { 8 | const factory GoalModel({ 9 | required String name, 10 | required double goal, 11 | required double change, 12 | required int reached, 13 | }) = _GoalModel; 14 | 15 | const GoalModel._(); 16 | 17 | factory GoalModel.fromJson(Map json) => _$GoalModelFromJson(json); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/models/offer/offer_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/data/models/offer/offer_type.dart'; 3 | import 'package:vizier/data/models/offer/slider_item/offer_slider_model.dart'; 4 | 5 | part 'offer_model.g.dart'; 6 | part 'offer_model.freezed.dart'; 7 | 8 | @freezed 9 | class OfferModel with _$OfferModel { 10 | const factory OfferModel({ 11 | required double expectedValue, 12 | required String label, 13 | required String image, 14 | required String detailsImage, 15 | required List sliders, 16 | required OfferType type, 17 | }) = _OfferModel; 18 | 19 | const OfferModel._(); 20 | 21 | factory OfferModel.fromJson(Map json) => _$OfferModelFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /lib/data/models/offer/offer_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/l10n/app_loc.dart'; 3 | 4 | enum OfferType { 5 | retirementGoals, 6 | mortgage, 7 | loans, 8 | } 9 | 10 | extension OfferTypeExtension on OfferType { 11 | String leftContent(BuildContext context) { 12 | switch (this) { 13 | case OfferType.retirementGoals: 14 | return AppLoc.of(context).offerTypeRetirementGoalsLeftContent; 15 | case OfferType.mortgage: 16 | return AppLoc.of(context).offerTypeMortgagesLeftContent; 17 | case OfferType.loans: 18 | return AppLoc.of(context).offerTypeLoansLeftContent; 19 | } 20 | } 21 | 22 | String rightContent(BuildContext context) { 23 | switch (this) { 24 | default: 25 | return AppLoc.of(context).offerTypeRightContent; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/data/models/offer/slider_item/offer_slider_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/data/models/offer/slider_item/offer_slider_type.dart'; 3 | 4 | part 'offer_slider_model.g.dart'; 5 | part 'offer_slider_model.freezed.dart'; 6 | 7 | @freezed 8 | class OfferSliderModel with _$OfferSliderModel { 9 | const factory OfferSliderModel({ 10 | required double current, 11 | required int frequency, 12 | required String label, 13 | required int maxValue, 14 | required OfferSliderType type, 15 | }) = _OfferSliderModel; 16 | 17 | const OfferSliderModel._(); 18 | 19 | factory OfferSliderModel.fromJson(Map json) => _$OfferSliderModelFromJson(json); 20 | 21 | double get actualValue => current * frequency; 22 | } 23 | -------------------------------------------------------------------------------- /lib/data/models/offer/slider_item/offer_slider_type.dart: -------------------------------------------------------------------------------- 1 | enum OfferSliderType { 2 | price, 3 | year, 4 | month, 5 | } 6 | -------------------------------------------------------------------------------- /lib/data/models/portfolio_overview/portfolio_overview_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'portfolio_overview_model.g.dart'; 4 | part 'portfolio_overview_model.freezed.dart'; 5 | 6 | @freezed 7 | class PortfolioOverviewModel with _$PortfolioOverviewModel { 8 | const factory PortfolioOverviewModel({ 9 | required double assetsBalance, 10 | required double assetsChange, 11 | required double assetsPercentageChange, 12 | required double digitalCurrenciesBalance, 13 | required double digitalCurrenciesPercentageChange, 14 | }) = _PortfolioOverviewModel; 15 | 16 | const PortfolioOverviewModel._(); 17 | 18 | factory PortfolioOverviewModel.fromJson(Map json) => _$PortfolioOverviewModelFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/models/retirement_plan/data/retirement_plan_data_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'retirement_plan_data_model.g.dart'; 4 | part 'retirement_plan_data_model.freezed.dart'; 5 | 6 | @freezed 7 | class RetirementPlanDataModel with _$RetirementPlanDataModel { 8 | const factory RetirementPlanDataModel({ 9 | required DateTime date, 10 | required double value, 11 | }) = _RetirementPlanDataModel; 12 | 13 | const RetirementPlanDataModel._(); 14 | 15 | factory RetirementPlanDataModel.fromJson(Map json) => _$RetirementPlanDataModelFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/data/models/retirement_plan/retirement_plan_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/data/models/retirement_plan/data/retirement_plan_data_model.dart'; 3 | 4 | part 'retirement_plan_model.g.dart'; 5 | part 'retirement_plan_model.freezed.dart'; 6 | 7 | @freezed 8 | class RetirementPlanModel with _$RetirementPlanModel { 9 | const factory RetirementPlanModel({ 10 | required List data, 11 | required DateTime from, 12 | required DateTime to, 13 | }) = _RetirementPlanModel; 14 | 15 | const RetirementPlanModel._(); 16 | 17 | factory RetirementPlanModel.fromJson(Map json) => _$RetirementPlanModelFromJson(json); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/models/transaction/category/transaction_category_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'transaction_category_model.g.dart'; 4 | part 'transaction_category_model.freezed.dart'; 5 | 6 | @freezed 7 | class TransactionCategoryModel with _$TransactionCategoryModel { 8 | const factory TransactionCategoryModel({ 9 | required String colorHex, 10 | required String name, 11 | required double value, 12 | required double percent, 13 | }) = _TransactionCategoryModel; 14 | 15 | const TransactionCategoryModel._(); 16 | 17 | factory TransactionCategoryModel.fromJson(Map json) => _$TransactionCategoryModelFromJson(json); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/models/transaction/transaction_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'transaction_model.g.dart'; 4 | part 'transaction_model.freezed.dart'; 5 | 6 | @freezed 7 | class TransactionModel with _$TransactionModel { 8 | const factory TransactionModel({ 9 | required String categoryName, 10 | required DateTime date, 11 | required String name, 12 | required double value, 13 | String? backgroundColorHex, 14 | String? image, 15 | }) = _TransactionModel; 16 | 17 | const TransactionModel._(); 18 | 19 | factory TransactionModel.fromJson(Map json) => _$TransactionModelFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/models/user/user_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'user_model.g.dart'; 4 | part 'user_model.freezed.dart'; 5 | 6 | @freezed 7 | class UserModel with _$UserModel { 8 | const factory UserModel({ 9 | required String avatar, 10 | required String email, 11 | required String firstName, 12 | required String lastName, 13 | }) = _UserModel; 14 | 15 | const UserModel._(); 16 | 17 | factory UserModel.fromJson(Map json) => _$UserModelFromJson(json); 18 | 19 | String get fullName => '$firstName $lastName'; 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/models/wallet/wallet_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:vizier/data/models/account/account_model.dart'; 3 | 4 | part 'wallet_model.g.dart'; 5 | part 'wallet_model.freezed.dart'; 6 | 7 | @freezed 8 | class WalletModel with _$WalletModel { 9 | const factory WalletModel({ 10 | required double balance, 11 | required double goal, 12 | required List accounts, 13 | required DateTime date, 14 | }) = _WalletModel; 15 | 16 | const WalletModel._(); 17 | 18 | factory WalletModel.fromJson(Map json) => _$WalletModelFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/repositories/authentication/authentication_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/requests/authentication/authentication_request.dart'; 2 | import 'package:vizier/data/responses/authentication/authentication_response.dart'; 3 | import 'package:vizier/data/responses/response_status.dart'; 4 | 5 | abstract class AuthenticationRepository { 6 | Future> login(AuthenticationRequest request); 7 | } 8 | -------------------------------------------------------------------------------- /lib/data/repositories/authentication/data_authentication_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/data/data_sources/authentication/authentication_data_source.dart'; 3 | import 'package:vizier/data/repositories/authentication/authentication_repository.dart'; 4 | import 'package:vizier/data/requests/authentication/authentication_request.dart'; 5 | import 'package:vizier/data/responses/authentication/authentication_response.dart'; 6 | import 'package:vizier/data/responses/response_status.dart'; 7 | 8 | @Injectable(as: AuthenticationRepository) 9 | class DataAuthenticationRepository extends AuthenticationRepository { 10 | final AuthenticationDataSource authenticationDataSource; 11 | 12 | DataAuthenticationRepository(this.authenticationDataSource); 13 | 14 | @override 15 | Future> login( 16 | AuthenticationRequest request, 17 | ) async { 18 | try { 19 | final ResponseStatus response = 20 | await authenticationDataSource.getLogin(request); 21 | return ResponseStatus.success(response.data!); 22 | } catch (error) { 23 | return ResponseStatus.error(error); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/data/repositories/finances/finances_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/finances_overview/finances_overview_model.dart'; 2 | import 'package:vizier/data/models/financial_history/financial_history_model.dart'; 3 | import 'package:vizier/data/models/retirement_plan/retirement_plan_model.dart'; 4 | import 'package:vizier/data/responses/response_status.dart'; 5 | 6 | abstract class FinancesRepository { 7 | Future> overview(); 8 | Future> history({ 9 | required int daysBack, 10 | }); 11 | Future> retirementPlan({ 12 | required int daysTo, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /lib/data/repositories/offers/data_offers_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/data/data_sources/offers/offers_data_source.dart'; 3 | import 'package:vizier/data/models/offer/offer_model.dart'; 4 | import 'package:vizier/data/repositories/offers/offers_repository.dart'; 5 | import 'package:vizier/data/responses/response_status.dart'; 6 | 7 | @Injectable(as: OffersRepository) 8 | class DataOffersRepository extends OffersRepository { 9 | final OffersDataSource offersDataSource; 10 | 11 | DataOffersRepository({ 12 | required this.offersDataSource, 13 | }); 14 | 15 | @override 16 | Future>> all() async { 17 | try { 18 | final ResponseStatus> response = 19 | await offersDataSource.all(); 20 | return ResponseStatus>.success(response.data!); 21 | } catch (error) { 22 | return ResponseStatus>.error(error); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/data/repositories/offers/offers_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/offer/offer_model.dart'; 2 | import 'package:vizier/data/responses/response_status.dart'; 3 | 4 | abstract class OffersRepository { 5 | Future>> all(); 6 | } 7 | -------------------------------------------------------------------------------- /lib/data/repositories/portfolio/portfolio_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/company_asset/company_asset_model.dart'; 2 | import 'package:vizier/data/models/company_listing/details/company_listing_details_item_model.dart'; 3 | import 'package:vizier/data/models/company_listing/history/company_listing_history_model.dart'; 4 | import 'package:vizier/data/models/portfolio_overview/portfolio_overview_model.dart'; 5 | import 'package:vizier/data/responses/response_status.dart'; 6 | 7 | abstract class PortfolioRepository { 8 | Future>> watchlist(); 9 | Future> overview(); 10 | Future>> details(); 11 | Future> history({ 12 | required CompanyAssetModel companyAsset, 13 | required int daysBack, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /lib/data/repositories/user/data_user_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/data/data_sources/user/user_data_source.dart'; 3 | import 'package:vizier/data/models/user/user_model.dart'; 4 | import 'package:vizier/data/repositories/user/user_repository.dart'; 5 | import 'package:vizier/data/responses/response_status.dart'; 6 | import 'package:vizier/data/responses/user/notifications/user_notifications_response.dart'; 7 | 8 | @Injectable(as: UserRepository) 9 | class DataUserRepository extends UserRepository { 10 | final UserDataSource userDataSource; 11 | 12 | DataUserRepository({ 13 | required this.userDataSource, 14 | }); 15 | 16 | @override 17 | Future> me() async { 18 | try { 19 | final ResponseStatus response = await userDataSource.me(); 20 | return ResponseStatus.success(response.data!); 21 | } catch (error) { 22 | return ResponseStatus.error(error); 23 | } 24 | } 25 | 26 | @override 27 | Future> notifications() async { 28 | try { 29 | final ResponseStatus response = 30 | await userDataSource.notifications(); 31 | return ResponseStatus.success(response.data!); 32 | } catch (error) { 33 | return ResponseStatus.error(error); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/data/repositories/user/user_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/user/user_model.dart'; 2 | import 'package:vizier/data/responses/response_status.dart'; 3 | import 'package:vizier/data/responses/user/notifications/user_notifications_response.dart'; 4 | 5 | abstract class UserRepository { 6 | Future> me(); 7 | Future> notifications(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/data/repositories/wallet/wallet_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/account/account_model.dart'; 2 | import 'package:vizier/data/models/account/breakdown/account_breakdown_model.dart'; 3 | import 'package:vizier/data/models/goal/goal_model.dart'; 4 | import 'package:vizier/data/models/transaction/transaction_model.dart'; 5 | import 'package:vizier/data/models/wallet/wallet_model.dart'; 6 | import 'package:vizier/data/responses/response_status.dart'; 7 | 8 | abstract class WalletRepository { 9 | Future> addWalletAccount({ 10 | required AccountModel account, 11 | }); 12 | Future> accountBreakdown({ 13 | required int daysBack, 14 | required AccountModel account, 15 | }); 16 | Future>> goals(); 17 | Future> wallet(); 18 | Future>> transactionHistory({ 19 | required AccountModel accountModel, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/requests/authentication/authentication_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'authentication_request.g.dart'; 4 | part 'authentication_request.freezed.dart'; 5 | 6 | @freezed 7 | class AuthenticationRequest with _$AuthenticationRequest { 8 | const factory AuthenticationRequest({ 9 | required String email, 10 | required String password, 11 | }) = _AuthenticationRequest; 12 | 13 | factory AuthenticationRequest.fromJson(Map json) => _$AuthenticationRequestFromJson(json); 14 | } 15 | -------------------------------------------------------------------------------- /lib/data/responses/authentication/authentication_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'authentication_response.g.dart'; 4 | 5 | @JsonSerializable( 6 | createToJson: false, 7 | ) 8 | class AuthenticationResponse { 9 | final String token; 10 | 11 | AuthenticationResponse({ 12 | required this.token, 13 | }); 14 | 15 | factory AuthenticationResponse.fromJson(Map json) => _$AuthenticationResponseFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/data/responses/response_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/errors/app_error.dart'; 2 | 3 | class ResponseStatus { 4 | final T? data; 5 | final AppError? error; 6 | 7 | ResponseStatus._({ 8 | this.data, 9 | this.error, 10 | }); 11 | 12 | factory ResponseStatus.success(T data) { 13 | return ResponseStatus._( 14 | data: data, 15 | ); 16 | } 17 | 18 | factory ResponseStatus.error(Object error) { 19 | final AppError appError = AppError.unknownFailure(error); 20 | return ResponseStatus._( 21 | error: appError, 22 | ); 23 | } 24 | 25 | void result({ 26 | required Function(T) onSuccess, 27 | required Function(AppError) onError, 28 | }) { 29 | if (data != null) { 30 | onSuccess(data as T); 31 | } else if (error != null) { 32 | onError(error!); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/data/responses/user/notifications/user_notifications_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'user_notifications_response.g.dart'; 4 | 5 | @JsonSerializable( 6 | createToJson: false, 7 | ) 8 | class UserNotificationsResponse { 9 | final bool areNotificationsEnabled; 10 | 11 | UserNotificationsResponse({ 12 | required this.areNotificationsEnabled, 13 | }); 14 | 15 | factory UserNotificationsResponse.fromJson(Map json) => _$UserNotificationsResponseFromJson(json); 16 | } 17 | -------------------------------------------------------------------------------- /lib/data/storages/common_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/data/storages/storage.dart'; 3 | import 'package:vizier/data/storages/storage_keys.dart'; 4 | 5 | 6 | @injectable 7 | class CommonStorage { 8 | final Storage storage; 9 | 10 | CommonStorage({ 11 | required this.storage, 12 | }); 13 | 14 | Future getIsFirstLaunch() async { 15 | final bool? isFirstLaunch = await storage.getBool( 16 | key: StorageKeys.firstLaunch, 17 | ); 18 | return isFirstLaunch ?? true; 19 | } 20 | 21 | Future storeIsFirstLaunch({ 22 | required bool isFirstLaunch, 23 | }) async { 24 | return storage.putBool( 25 | key: StorageKeys.firstLaunch, 26 | value: isFirstLaunch, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/data/storages/secure_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | 4 | 5 | 6 | @injectable 7 | class SecureStorage { 8 | final FlutterSecureStorage flutterSecureStorage; 9 | 10 | SecureStorage({ 11 | required this.flutterSecureStorage, 12 | }); 13 | 14 | Future putString({ 15 | required String key, 16 | required String value, 17 | }) async { 18 | return flutterSecureStorage.write( 19 | key: key, 20 | value: value, 21 | ); 22 | } 23 | 24 | Future getString({ 25 | required String key, 26 | }) async { 27 | return flutterSecureStorage.read( 28 | key: key, 29 | ); 30 | } 31 | 32 | Future remove({ 33 | required String key, 34 | }) async { 35 | return flutterSecureStorage.delete( 36 | key: key, 37 | ); 38 | } 39 | 40 | Future removeAll() async { 41 | return flutterSecureStorage.deleteAll(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/data/storages/storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | @injectable 5 | class Storage { 6 | final SharedPreferences sharedPreferences; 7 | 8 | Storage({ 9 | required this.sharedPreferences, 10 | }); 11 | 12 | Future putBool({ 13 | required String key, 14 | required bool value, 15 | }) async { 16 | return 17 | sharedPreferences.setBool(key, value); 18 | } 19 | 20 | Future getBool({ 21 | required String key, 22 | }) async { 23 | return sharedPreferences.getBool(key); 24 | } 25 | 26 | Future putString({ 27 | required String key, 28 | required String value, 29 | }) async { 30 | return sharedPreferences.setString(key, value); 31 | } 32 | 33 | Future getString({ 34 | required String key, 35 | }) async { 36 | return sharedPreferences.getString(key); 37 | } 38 | 39 | Future putInt({ 40 | required String key, 41 | required int value, 42 | }) async { 43 | return sharedPreferences.setInt(key, value); 44 | } 45 | 46 | Future getInt({ 47 | required String key, 48 | }) async { 49 | return sharedPreferences.getInt(key); 50 | } 51 | 52 | Future putDouble({ 53 | required String key, 54 | required double value, 55 | }) async { 56 | return sharedPreferences.setDouble(key, value); 57 | } 58 | 59 | Future getDouble({ 60 | required String key, 61 | }) async { 62 | return sharedPreferences.getDouble(key); 63 | } 64 | 65 | Future remove({ 66 | required String key, 67 | }) async { 68 | return sharedPreferences.remove(key); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/data/storages/storage_keys.dart: -------------------------------------------------------------------------------- 1 | abstract class StorageKeys { 2 | const StorageKeys._(); 3 | 4 | static const String firstLaunch = 'key-first-launch'; 5 | static const String accessToken = 'key-access-token'; 6 | } 7 | -------------------------------------------------------------------------------- /lib/data/storages/token_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:vizier/data/storages/secure_storage.dart'; 3 | import 'package:vizier/data/storages/storage_keys.dart'; 4 | 5 | @injectable 6 | class TokenStorage { 7 | final SecureStorage secureStorage; 8 | 9 | TokenStorage({ 10 | required this.secureStorage, 11 | }); 12 | 13 | Future storeAccessToken(String accessToken) { 14 | return secureStorage.putString( 15 | key: StorageKeys.accessToken, 16 | value: accessToken, 17 | ); 18 | } 19 | 20 | Future getAccessToken() { 21 | return secureStorage.getString( 22 | key: StorageKeys.accessToken, 23 | ); 24 | } 25 | 26 | Future hasToken() async { 27 | final String? accessToken = await getAccessToken(); 28 | return accessToken != null; 29 | } 30 | 31 | Future removeAccessToken() { 32 | return secureStorage.remove( 33 | key: StorageKeys.accessToken, 34 | ); 35 | } 36 | 37 | Future clear() { 38 | return Future.wait([ 39 | removeAccessToken(), 40 | ]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/extensions/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:collection/collection.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:vizier/data/models/company_listing/history/data/company_listing_history_data_model.dart'; 6 | import 'package:vizier/data/models/financial_history/history_data/financial_history_data_model.dart'; 7 | 8 | part 'core_extensions.dart'; 9 | part 'model_extensions.dart'; 10 | part 'ui_extensions.dart'; 11 | -------------------------------------------------------------------------------- /lib/extensions/model_extensions.dart: -------------------------------------------------------------------------------- 1 | part of 'extensions.dart'; 2 | 3 | extension ListFinancialHistoryDataModelMapper 4 | on List { 5 | double get highestBalanceOrSpent => 6 | (copy().sorted( 7 | (a, b) => a.maxBalanceOrSpent.compareTo(b.maxBalanceOrSpent), 8 | )).lastOrNull?.maxBalanceOrSpent ?? 9 | 0.0; 10 | 11 | List sortedByDate() => copy().sorted( 12 | (a, b) => a.date.compareTo(b.date), 13 | ); 14 | } 15 | 16 | extension ListPortfolioCurrenciesHistoryDataModelMapper 17 | on List { 18 | double get highestValues => 19 | (copy().sorted( 20 | (a, b) => a.maxValues.compareTo(b.maxValues), 21 | )).lastOrNull?.maxValues ?? 22 | 0.0; 23 | 24 | double get lowestValues => 25 | (copy().sorted( 26 | (a, b) => a.minValues.compareTo(b.minValues), 27 | )).firstOrNull?.minValues ?? 28 | 0.0; 29 | 30 | List sortedByDate() => copy().sorted( 31 | (a, b) => a.date.compareTo(b.date), 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /lib/extensions/ui_extensions.dart: -------------------------------------------------------------------------------- 1 | part of 'extensions.dart'; 2 | 3 | extension ListTextPainterExtensions on List { 4 | void calculate(Size size) { 5 | for (int i = 0; i < length; i++) { 6 | this[i].layout( 7 | maxWidth: size.width, 8 | ); 9 | } 10 | } 11 | 12 | double get biggestHeight => isNotEmpty 13 | ? (copy().sorted((a, b) => a.height.compareTo(b.height))).last.height 14 | : 0.0; 15 | 16 | double get biggestWidth => isNotEmpty 17 | ? (copy().sorted((a, b) => a.width.compareTo(b.width))).last.width 18 | : 0.0; 19 | 20 | double get firstHeightOrZero => isNotEmpty ? first.height : 0.0; 21 | 22 | double get lastHeightOrZero => isNotEmpty ? last.height : 0.0; 23 | 24 | double get averageWidthOrZero => 25 | isNotEmpty ? (first.width + last.width).half : 0.0; 26 | } 27 | -------------------------------------------------------------------------------- /lib/l10n/app_loc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | 4 | class AppLoc { 5 | const AppLoc._(); 6 | 7 | static AppLocalizations of(BuildContext context) { 8 | if (AppLocalizations.of(context) != null) { 9 | return AppLocalizations.of(context)!; 10 | } else { 11 | throw _AppLocalizationsContextNotAvailableException(); 12 | } 13 | } 14 | } 15 | 16 | class _AppLocalizationsContextNotAvailableException implements Exception { 17 | @override 18 | String toString() { 19 | return 'Provided context is ether null or doesn\'t contain AppLocalizations'; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/injector/di.dart'; 3 | import 'package:vizier/data/storages/common_storage.dart'; 4 | import 'package:vizier/data/storages/token_storage.dart'; 5 | import 'package:vizier/ui/main_app.dart'; 6 | 7 | Future main() async { 8 | WidgetsFlutterBinding.ensureInitialized(); 9 | await DI.instance.setupInjection(); 10 | final bool isFirstLaunch = await DI.resolve().getIsFirstLaunch(); 11 | final bool isSessionActive = await DI.resolve().hasToken(); 12 | 13 | runApp( 14 | MainApp( 15 | isFirstTimeOpened: isFirstLaunch, 16 | isSessionActive: isSessionActive, 17 | ), 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /lib/mocks/factories/duration_factory.dart: -------------------------------------------------------------------------------- 1 | part of '../mock_factory.dart'; 2 | 3 | class _DurationFactory { 4 | final DummyUtil dummyUtil; 5 | 6 | _DurationFactory({ 7 | required this.dummyUtil, 8 | }); 9 | 10 | static _DurationFactory instance = _DurationFactory( 11 | dummyUtil: DI.resolve(), 12 | ); 13 | 14 | Duration standard(int days) { 15 | switch (days) { 16 | case DateTime.daysPerWeek: 17 | return const Duration( 18 | days: 6, 19 | ); 20 | case DateTime.monthsPerYear: 21 | return const Duration( 22 | days: 30, 23 | ); 24 | case DateTime.monthsPerYear * 30: 25 | return const Duration( 26 | days: DateTime.monthsPerYear * 30, 27 | ); 28 | default: 29 | return const Duration( 30 | days: DateTime.monthsPerYear * 30 * 4, 31 | ); 32 | } 33 | } 34 | 35 | Duration indexBased(int days, int index) { 36 | switch (days) { 37 | case DateTime.daysPerWeek: 38 | return Duration( 39 | days: index, 40 | ); 41 | case DateTime.monthsPerYear: 42 | return Duration( 43 | days: 7 * index, 44 | ); 45 | case DateTime.monthsPerYear * 30: 46 | return Duration( 47 | days: 90 * index, 48 | ); 49 | default: 50 | return Duration( 51 | days: 365 * index, 52 | ); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/mocks/factories/transaction_category_factory.dart: -------------------------------------------------------------------------------- 1 | part of '../mock_factory.dart'; 2 | 3 | class _TransactionCategoryFactory { 4 | final DummyUtil dummyUtil; 5 | 6 | _TransactionCategoryFactory({ 7 | required this.dummyUtil, 8 | }); 9 | 10 | static _TransactionCategoryFactory instance = _TransactionCategoryFactory( 11 | dummyUtil: DI.resolve(), 12 | ); 13 | 14 | final List _names = [ 15 | 'Life', 16 | 'Housing', 17 | 'Debt repayment', 18 | 'Savings', 19 | 'Transporation', 20 | ]..shuffle(); 21 | 22 | static const List _colorsFromLowestValue = [ 23 | 'FFFFD6B4', 24 | 'FFFF9843', 25 | 'FFFF7000', 26 | 'FFB783FD', 27 | 'FF8043F9', 28 | ]; 29 | 30 | List transactionCategories() { 31 | final List generatedValues = List.generate( 32 | _names.length, 33 | (index) => dummyUtil.randomDouble( 34 | minNumber: 100, 35 | maxNumber: 1500, 36 | ), 37 | ).sorted((a, b) => a.compareTo(b)); 38 | final double summaryValues = generatedValues.reduce( 39 | (value, element) => value + element, 40 | ); 41 | return List.generate( 42 | generatedValues.length, 43 | (index) => TransactionCategoryModel( 44 | colorHex: _colorsFromLowestValue[index], 45 | name: _names[index], 46 | value: generatedValues[index], 47 | percent: generatedValues[index] / summaryValues * 100, 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/mocks/offers_mocks.dart: -------------------------------------------------------------------------------- 1 | part of 'mock_factory.dart'; 2 | 3 | class _OffersMocks { 4 | final double goal; 5 | 6 | _OffersMocks({ 7 | required this.goal, 8 | }); 9 | 10 | List prepareAll() { 11 | return [ 12 | OfferModel( 13 | expectedValue: goal, 14 | label: 'Retirement goals', 15 | image: AppImages.svg.retirementGoals, 16 | detailsImage: AppImages.svg.retirementGoalsDetails, 17 | sliders: [ 18 | const OfferSliderModel( 19 | current: 0.4, 20 | frequency: 100, 21 | label: 'Mothly saving plan', 22 | maxValue: 10000, 23 | type: OfferSliderType.price, 24 | ), 25 | const OfferSliderModel( 26 | current: 0.6, 27 | frequency: 1000, 28 | label: 'Current asset worth', 29 | maxValue: 100000, 30 | type: OfferSliderType.price, 31 | ), 32 | ], 33 | type: OfferType.retirementGoals, 34 | ), 35 | OfferModel( 36 | expectedValue: goal * 0.3, 37 | label: 'Mortgage', 38 | image: AppImages.svg.mortgage, 39 | detailsImage: AppImages.svg.mortgageDetails, 40 | sliders: [ 41 | const OfferSliderModel( 42 | current: 0.2, 43 | frequency: 10000, 44 | label: 'Purchase price', 45 | maxValue: 1000000, 46 | type: OfferSliderType.price, 47 | ), 48 | const OfferSliderModel( 49 | current: 0.3, 50 | frequency: 1, 51 | label: 'Repayment time', 52 | maxValue: 30, 53 | type: OfferSliderType.year, 54 | ), 55 | ], 56 | type: OfferType.mortgage, 57 | ), 58 | ]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/mocks/user_mocks.dart: -------------------------------------------------------------------------------- 1 | part of 'mock_factory.dart'; 2 | 3 | class _UserMocks { 4 | final DummyUtil dummyUtil; 5 | 6 | _UserMocks({ 7 | required this.dummyUtil, 8 | }); 9 | 10 | UserModel prepareUser() { 11 | return const UserModel( 12 | avatar: 'assets/mocks/user/avatar.png', 13 | email: 'John_S@gmail.com', 14 | firstName: 'John', 15 | lastName: 'Smith', 16 | ); 17 | } 18 | 19 | UserNotificationsResponse prepareUserNotificationsResponse() { 20 | return UserNotificationsResponse( 21 | areNotificationsEnabled: dummyUtil.randomBool(), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/ui/app_coordinator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:vizier/config/router/app_router.dart'; 4 | import 'package:vizier/cubits/authentication/authentication_cubit.dart'; 5 | import 'package:vizier/cubits/user/user_cubit.dart'; 6 | import 'package:vizier/cubits/wallet/wallet_cubit.dart'; 7 | 8 | //This class should add listeners to global cubits if 9 | //there is need to use Global context (eg. show page after push notification) 10 | class AppCoordinator extends StatelessWidget { 11 | final AppRouter appRouter; 12 | final Widget child; 13 | 14 | const AppCoordinator({ 15 | required this.appRouter, 16 | required this.child, 17 | super.key, 18 | }); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return MultiBlocListener( 23 | listeners: [ 24 | _authenticationListener(), 25 | ], 26 | child: child, 27 | ); 28 | } 29 | 30 | BlocListener _authenticationListener() { 31 | return BlocListener( 32 | listener: (context, state) { 33 | state.maybeMap( 34 | authenticated: (_) => context.read().fetchMe(), 35 | logout: (_) { 36 | context.read().logout(); 37 | context.read().logout(); 38 | appRouter.pushAndPopUntil( 39 | const AuthenticationLoginPageRoute(), 40 | predicate: (_) => false, 41 | ); 42 | }, 43 | orElse: () => null, 44 | ); 45 | }, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/ui/modals/bottom_actions_sheet/bottom_actions_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/styles/colors/app_colors.dart'; 3 | import 'package:vizier/config/styles/dimensions/app_dimensions.dart'; 4 | import 'package:vizier/config/styles/text_styles/app_text_styles.dart'; 5 | import 'package:vizier/ui/widgets/adaptive/adaptive_button.dart'; 6 | 7 | part 'widget/bottom_sheet_action_cell.dart'; 8 | 9 | class BottomActionsSheet extends StatelessWidget { 10 | final List actions; 11 | 12 | const BottomActionsSheet({ 13 | required this.actions, 14 | super.key, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Padding( 20 | padding: EdgeInsets.symmetric( 21 | horizontal: AppDimensions.padding.bigValue, 22 | vertical: AppDimensions.padding.defaultValue, 23 | ), 24 | child: Column( 25 | mainAxisSize: MainAxisSize.min, 26 | crossAxisAlignment: CrossAxisAlignment.start, 27 | children: [ 28 | Center( 29 | child: _buildStick(), 30 | ), 31 | SizedBox( 32 | height: AppDimensions.padding.smallValue, 33 | ), 34 | ...actions, 35 | ], 36 | ), 37 | ); 38 | } 39 | 40 | Widget _buildStick() { 41 | return Container( 42 | decoration: BoxDecoration( 43 | borderRadius: BorderRadius.circular(2.5), 44 | color: AppColors.gray400, 45 | ), 46 | height: 4.0, 47 | width: 51.0, 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/ui/modals/bottom_actions_sheet/bottom_actions_sheet_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/styles/decorations/app_decorations.dart'; 3 | import 'package:vizier/l10n/app_loc.dart'; 4 | import 'package:vizier/ui/modals/bottom_actions_sheet/bottom_actions_sheet.dart'; 5 | 6 | abstract class BottomActionsSheetFactory { 7 | const BottomActionsSheetFactory._(); 8 | 9 | static void _showBottomSheet( 10 | BuildContext context, { 11 | required Widget child, 12 | }) { 13 | showModalBottomSheet( 14 | shape: AppDecorations.bottomSheetShape, 15 | context: context, 16 | builder: (context) => child, 17 | ); 18 | } 19 | 20 | static void showWalletMoreModal( 21 | BuildContext context, { 22 | required VoidCallback onCardsOverviewPressed, 23 | }) { 24 | _showBottomSheet( 25 | context, 26 | child: BottomActionsSheet( 27 | actions: [ 28 | BottomSheetActionCell( 29 | title: AppLoc.of(context).financesHistoryChartBottomSheetCardsOverviewButton, 30 | onPressed: () { 31 | Navigator.of(context).pop(); 32 | onCardsOverviewPressed(); 33 | }, 34 | ), 35 | ], 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/ui/modals/bottom_actions_sheet/widget/bottom_sheet_action_cell.dart: -------------------------------------------------------------------------------- 1 | part of '../bottom_actions_sheet.dart'; 2 | 3 | class BottomSheetActionCell extends StatelessWidget { 4 | final String title; 5 | final VoidCallback onPressed; 6 | 7 | const BottomSheetActionCell({ 8 | required this.title, 9 | required this.onPressed, 10 | super.key, 11 | }); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return AdaptiveButton( 16 | onPressed: onPressed, 17 | child: Text( 18 | title, 19 | style: AppTextStyles.caption1(), 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/ui/models/card_account_type.dart: -------------------------------------------------------------------------------- 1 | enum CardAccountType { 2 | front, 3 | back, 4 | } 5 | -------------------------------------------------------------------------------- /lib/ui/models/chart_multi_pie_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ChartMultiPieItem { 4 | final double amount; 5 | final Color color; 6 | final String name; 7 | 8 | const ChartMultiPieItem({ 9 | required this.amount, 10 | required this.color, 11 | required this.name, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /lib/ui/models/chart_multi_pie_section.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/ui/models/chart_multi_pie_item.dart'; 2 | 3 | class ChartMultiPieSection { 4 | final List items; 5 | 6 | const ChartMultiPieSection({ 7 | required this.items, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/ui/pages/app/main_app_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 4 | import 'package:vizier/config/router/app_router.dart'; 5 | import 'package:vizier/config/styles/theme/app_theme.dart'; 6 | 7 | class MainAppPage extends StatefulWidget { 8 | final AppRouter appRouter; 9 | final bool isFirstTimeOpened; 10 | final bool isSessionActive; 11 | 12 | const MainAppPage({ 13 | required this.appRouter, 14 | required this.isFirstTimeOpened, 15 | required this.isSessionActive, 16 | super.key, 17 | }); 18 | 19 | @override 20 | _MainAppPageState createState() => _MainAppPageState(); 21 | } 22 | 23 | class _MainAppPageState extends State { 24 | @override 25 | Widget build(BuildContext context) { 26 | return MaterialApp.router( 27 | localizationsDelegates: const [ 28 | AppLocalizations.delegate, 29 | ], 30 | routerDelegate: widget.appRouter.delegate( 31 | initialRoutes: [ 32 | _prepareInitialRoute(), 33 | ], 34 | ), 35 | routeInformationParser: widget.appRouter.defaultRouteParser(), 36 | theme: AppTheme.fromType(ThemeType.light).themeData, 37 | ); 38 | } 39 | 40 | PageRouteInfo _prepareInitialRoute() { 41 | if (widget.isFirstTimeOpened) { 42 | return const OnboardingPageRoute(); 43 | } else { 44 | if (widget.isSessionActive) { 45 | return const BottomNavigationPageRoute(); 46 | } 47 | return const AuthenticationLoginPageRoute(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/ui/pages/authentication/login/widgets/login_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:vizier/config/styles/colors/app_colors.dart'; 4 | import 'package:vizier/config/styles/images/app_images.dart'; 5 | import 'package:vizier/config/styles/text_styles/app_text_styles.dart'; 6 | import 'package:vizier/l10n/app_loc.dart'; 7 | 8 | class LoginHeader extends StatelessWidget { 9 | const LoginHeader({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | SvgPicture.asset( 16 | AppImages.svg.moneyLogin, 17 | ), 18 | const SizedBox( 19 | height: 20.0, 20 | ), 21 | _buildTitle(context), 22 | const SizedBox( 23 | height: 16.0, 24 | ), 25 | _buildSubTitle(context), 26 | ], 27 | ); 28 | } 29 | 30 | Widget _buildTitle(BuildContext context) { 31 | return Text( 32 | AppLoc.of(context).logInPageHeaderTitle, 33 | style: AppTextStyles.h2(), 34 | ); 35 | } 36 | 37 | Widget _buildSubTitle(BuildContext context) { 38 | return Text( 39 | AppLoc.of(context).logInPageHeaderSubtitle, 40 | style: AppTextStyles.text2().copyWith( 41 | color: AppColors.gray200, 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/ui/pages/bottom_navigation/bottom_navigation_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:vizier/config/router/app_router.dart'; 4 | import 'package:vizier/l10n/app_loc.dart'; 5 | 6 | enum BottomNavigationItem { home, finances, portfolio, offers } 7 | 8 | extension BottomNavigationItemExtension on BottomNavigationItem { 9 | String title(BuildContext context) { 10 | switch (this) { 11 | case BottomNavigationItem.home: 12 | return AppLoc.of(context).bottomBarItemHomeTitle; 13 | case BottomNavigationItem.finances: 14 | return AppLoc.of(context).bottomBarItemFinancesTitle; 15 | case BottomNavigationItem.portfolio: 16 | return AppLoc.of(context).bottomBarItemPortfolioTitle; 17 | case BottomNavigationItem.offers: 18 | return AppLoc.of(context).bottomBarItemOffersTitle; 19 | } 20 | } 21 | 22 | IconData get icon { 23 | //TODO (MT): Change icons 24 | switch (this) { 25 | case BottomNavigationItem.home: 26 | return Icons.home; 27 | case BottomNavigationItem.finances: 28 | return Icons.pie_chart_outline; 29 | case BottomNavigationItem.portfolio: 30 | return Icons.account_balance_wallet; 31 | case BottomNavigationItem.offers: 32 | return Icons.flag; 33 | } 34 | } 35 | 36 | PageRouteInfo get route { 37 | switch (this) { 38 | case BottomNavigationItem.home: 39 | return const HomePageRoute(); 40 | case BottomNavigationItem.finances: 41 | return const FinancesPageRoute(); 42 | case BottomNavigationItem.portfolio: 43 | return const PortfolioPageRoute(); 44 | case BottomNavigationItem.offers: 45 | return const OffersPageRoute(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/ui/pages/bottom_navigation/bottom_navigation_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:vizier/ui/pages/bottom_navigation/bottom_navigation_item.dart'; 4 | import 'package:vizier/ui/widgets/ripple_remover.dart'; 5 | 6 | class BottomNavigationPage extends StatefulWidget { 7 | static const String route = 'bottom_navigation'; 8 | 9 | const BottomNavigationPage({super.key}); 10 | 11 | @override 12 | _BottomNavigationPageState createState() => _BottomNavigationPageState(); 13 | } 14 | 15 | class _BottomNavigationPageState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | return AutoTabsScaffold( 19 | routes: BottomNavigationItem.values 20 | .map( 21 | (e) => e.route, 22 | ) 23 | .toList(), 24 | bottomNavigationBuilder: (_, tabsRouter) => RippleRemover( 25 | child: _buildBottomNavigationBar( 26 | tabsRouter: tabsRouter, 27 | ), 28 | ), 29 | ); 30 | } 31 | 32 | BottomNavigationBar _buildBottomNavigationBar({ 33 | required TabsRouter tabsRouter, 34 | }) { 35 | return BottomNavigationBar( 36 | currentIndex: tabsRouter.activeIndex, 37 | onTap: tabsRouter.setActiveIndex, 38 | items: BottomNavigationItem.values 39 | .map( 40 | (e) => BottomNavigationBarItem( 41 | icon: Icon(e.icon), 42 | label: e.title(context), 43 | ), 44 | ) 45 | .toList(), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/ui/pages/company_listing_details/widgets/company_listing_buttons.dart: -------------------------------------------------------------------------------- 1 | part of '../company_listing_page.dart'; 2 | 3 | class _CompanyListingButtons extends StatelessWidget { 4 | final VoidCallback onBuyPressed; 5 | final VoidCallback onSellPressed; 6 | 7 | const _CompanyListingButtons({ 8 | required this.onBuyPressed, 9 | required this.onSellPressed, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Row( 15 | children: [ 16 | Expanded( 17 | child: AdaptiveButton( 18 | onPressed: onSellPressed, 19 | decoration: AppDecorations.button.secondary().copyWith( 20 | color: Colors.transparent, 21 | ), 22 | child: Text( 23 | AppLoc.of(context).portfolioCurrenciesSellButton, 24 | style: AppTextStyles.button.primary(), 25 | ), 26 | ), 27 | ), 28 | SizedBox( 29 | width: AppDimensions.padding.defaultValue, 30 | ), 31 | Expanded( 32 | child: AdaptiveButton( 33 | onPressed: onBuyPressed, 34 | decoration: AppDecorations.button.primary(), 35 | child: Text( 36 | AppLoc.of(context).portfolioCurrenciesBuyButton, 37 | style: AppTextStyles.button.primary(), 38 | ), 39 | ), 40 | ), 41 | ], 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/ui/pages/company_listing_details/widgets/company_listing_history_chart.dart: -------------------------------------------------------------------------------- 1 | part of '../company_listing_page.dart'; 2 | 3 | class _CompanyListingHistoryChart extends StatelessWidget { 4 | final CompanyListingHistoryModel? data; 5 | final bool isLoading; 6 | final Function(ChartTab tab) onTabSelected; 7 | final ChartTab selectedTab; 8 | 9 | const _CompanyListingHistoryChart({ 10 | required this.isLoading, 11 | required this.onTabSelected, 12 | required this.selectedTab, 13 | this.data, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final CompanyListingHistoryModel? data = this.data; 19 | return ChartContainer( 20 | isLoading: isLoading, 21 | selectedTab: selectedTab, 22 | onTabSelected: onTabSelected, 23 | chart: Padding( 24 | padding: EdgeInsets.only( 25 | top: AppDimensions.padding.smallValue, 26 | ), 27 | child: data != null 28 | ? Chart( 29 | layers: ChartFactory.fromPortfolioCurrenciesHistoryModel(data), 30 | padding: const EdgeInsets.only( 31 | left: 20.0, 32 | bottom: 8.0, 33 | ), 34 | ) 35 | : const SizedBox.shrink(), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/ui/pages/credit_card/overview/widgets/credit_card_list.dart: -------------------------------------------------------------------------------- 1 | part of '../credit_card_overview.dart'; 2 | 3 | class _CreditCardList extends StatelessWidget { 4 | final List accounts; 5 | final PageController controller; 6 | final int currentIndex; 7 | final void Function(AccountModel, int) onCardChanged; 8 | 9 | const _CreditCardList({ 10 | required this.accounts, 11 | required this.controller, 12 | required this.currentIndex, 13 | required this.onCardChanged, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return PageView.builder( 19 | controller: controller, 20 | onPageChanged: (page) => onCardChanged(accounts[page], page), 21 | itemCount: accounts.length, 22 | itemBuilder: (_, index) { 23 | final AccountModel account = accounts[index]; 24 | return AnimatedScale( 25 | duration: AppConstants.animation.defaultDuration, 26 | scale: currentIndex == index ? 1.0 : 0.95, 27 | child: AnimatedOpacity( 28 | duration: AppConstants.animation.defaultDuration, 29 | opacity: currentIndex == index ? 1.0 : 0.5, 30 | child: AnimatedCreditCard( 31 | balance: CurrencyFormatterUtil.instance.format( 32 | value: account.balance, 33 | ), 34 | expiry: account.expiry, 35 | logo: account.logo, 36 | number: account.number, 37 | type: CardAccountType.front, 38 | ), 39 | ), 40 | ); 41 | }, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/ui/pages/credit_card/overview/widgets/credit_card_transactions_list.dart: -------------------------------------------------------------------------------- 1 | part of '../credit_card_overview.dart'; 2 | 3 | class _CreditCardTransactionsList extends StatelessWidget { 4 | final List transactions; 5 | 6 | const _CreditCardTransactionsList({ 7 | required this.transactions, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return ListView.separated( 13 | itemBuilder: (_, index) { 14 | final TransactionModel transaction = transactions[index]; 15 | return TransactionCell( 16 | transactionModel: transaction, 17 | ); 18 | }, 19 | itemCount: transactions.length, 20 | padding: AppDimensions.padding.defaultHorizontal().copyWith( 21 | bottom: MediaQuery.of(context).viewPadding.bottom, 22 | ), 23 | separatorBuilder: (_, __) => SizedBox( 24 | height: AppDimensions.padding.bigValue, 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/ui/pages/finances/widgets/finances_header.dart: -------------------------------------------------------------------------------- 1 | part of '../finances_page.dart'; 2 | 3 | class _FinancesHeader extends StatelessWidget { 4 | final double cashBalance; 5 | 6 | const _FinancesHeader({ 7 | required this.cashBalance, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Column( 13 | children: [ 14 | _buildTitle(context), 15 | _buildDescription(context), 16 | ], 17 | ); 18 | } 19 | 20 | Widget _buildTitle(BuildContext context) { 21 | return Text( 22 | CurrencyFormatterUtil.instance.format( 23 | value: cashBalance, 24 | ), 25 | style: Theme.of(context).textTheme.headline1, 26 | ); 27 | } 28 | 29 | Widget _buildDescription(BuildContext context) { 30 | return Text( 31 | AppLoc.of(context).financesHeaderDescription, 32 | style: AppTextStyles.caption2Bold().copyWith( 33 | color: AppColors.navy.withOpacity(0.5), 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/ui/pages/finances/widgets/finances_status_summary.dart: -------------------------------------------------------------------------------- 1 | part of '../finances_page.dart'; 2 | 3 | class _FinancesStatusSummary extends StatelessWidget { 4 | final double spent; 5 | final DateTime since; 6 | 7 | const _FinancesStatusSummary({ 8 | required this.spent, 9 | required this.since, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | decoration: AppDecorations.defaultBorder(), 16 | padding: EdgeInsets.all( 17 | AppDimensions.padding.defaultValue, 18 | ), 19 | child: Column( 20 | children: [ 21 | _buildTitle(context), 22 | _buildSpentAmount(context), 23 | _buildSince(context), 24 | ], 25 | ), 26 | ); 27 | } 28 | 29 | Widget _buildTitle(BuildContext context) { 30 | return Text( 31 | AppLoc.of(context).financesStatusSummaryTitle, 32 | style: AppTextStyles.caption2Bold().copyWith( 33 | color: AppColors.error300, 34 | ), 35 | ); 36 | } 37 | 38 | Widget _buildSpentAmount(BuildContext context) { 39 | return Text( 40 | CurrencyFormatterUtil.instance.format( 41 | value: spent, 42 | ), 43 | style: Theme.of(context).textTheme.headline4, 44 | ); 45 | } 46 | 47 | Widget _buildSince(BuildContext context) { 48 | final String date = DateFormatterUtil.instance.format( 49 | date: since, 50 | pattern: 'MMMM d', 51 | ) ?? 52 | ''; 53 | return Text( 54 | AppLoc.of(context).financesStatusSummarySince(date), 55 | style: AppTextStyles.caption2().copyWith( 56 | color: AppColors.navy.withOpacity(0.6), 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/ui/pages/financial_breakdown/financial_breakdown_page_arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:vizier/data/models/account/account_model.dart'; 2 | 3 | class FinancialBreakdownPageArguments { 4 | final AccountModel account; 5 | 6 | FinancialBreakdownPageArguments({ 7 | required this.account, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/ui/pages/financial_breakdown/widgets/financial_breakdown_transactions_summary.dart: -------------------------------------------------------------------------------- 1 | part of '../financial_breakdown_page.dart'; 2 | 3 | class _FinancialBreakdownTransactionsSummary extends StatelessWidget { 4 | final List transactionCategories; 5 | 6 | const _FinancialBreakdownTransactionsSummary({ 7 | required this.transactionCategories, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | decoration: const BoxDecoration( 14 | borderRadius: BorderRadius.vertical( 15 | top: Radius.circular(24.0), 16 | ), 17 | color: AppColors.white, 18 | ), 19 | padding: EdgeInsets.all(AppDimensions.padding.bigValue), 20 | child: SafeArea( 21 | child: _buildTransactionsList(), 22 | ), 23 | ); 24 | } 25 | 26 | Widget _buildTransactionsList() { 27 | return ListView.builder( 28 | itemBuilder: (context, index) => _FinancialBreakdownTransactionCell( 29 | transactionCategory: transactionCategories[index], 30 | ), 31 | itemCount: transactionCategories.length, 32 | physics: const NeverScrollableScrollPhysics(), 33 | shrinkWrap: true, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/ui/pages/home/add/widgets/home_add_accounts_section.dart: -------------------------------------------------------------------------------- 1 | part of '../home_add_content.dart'; 2 | 3 | class _HomeAddAccountsSection extends StatelessWidget { 4 | final VoidCallback onMorePressed; 5 | 6 | const _HomeAddAccountsSection({ 7 | required this.onMorePressed, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Column( 13 | children: [ 14 | SectionHeader( 15 | title: AppLoc.of(context).homeAddAccountsSectionTitle, 16 | onMorePressed: onMorePressed, 17 | ), 18 | DecoratedBox( 19 | decoration: AppDecorations.defaultBorder(), 20 | child: _buildActions(context), 21 | ), 22 | ], 23 | ); 24 | } 25 | 26 | Widget _buildActions(BuildContext context) { 27 | return Column(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/ui/pages/home/add/widgets/home_add_action_cell.dart: -------------------------------------------------------------------------------- 1 | part of '../home_add_content.dart'; 2 | 3 | class _HomeAddActionCell extends StatelessWidget { 4 | final String title; 5 | final IconData icon; 6 | final VoidCallback onPressed; 7 | 8 | const _HomeAddActionCell({ 9 | required this.title, 10 | required this.icon, 11 | required this.onPressed, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return AdaptiveButton( 17 | onPressed: onPressed, 18 | padding: const EdgeInsets.symmetric( 19 | horizontal: 8.0, 20 | vertical: 12.0, 21 | ), 22 | child: Row( 23 | children: [ 24 | _buildIcon(), 25 | const SizedBox( 26 | width: 12.0, 27 | ), 28 | _buildTitle(context), 29 | ], 30 | ), 31 | ); 32 | } 33 | 34 | Widget _buildIcon() { 35 | return CircleAvatar( 36 | backgroundColor: AppColors.primary100.withOpacity(0.16), 37 | radius: 21.0, 38 | child: Icon( 39 | icon, 40 | color: AppColors.primary100, 41 | ), 42 | ); 43 | } 44 | 45 | Widget _buildTitle(BuildContext context) { 46 | return Text( 47 | title, 48 | style: Theme.of(context).textTheme.headline6, 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/ui/pages/home/home_tab_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/l10n/app_loc.dart'; 3 | import 'package:vizier/ui/pages/home/add/home_add_content.dart'; 4 | import 'package:vizier/ui/pages/home/my_future/home_my_future_content.dart'; 5 | import 'package:vizier/ui/pages/home/my_wallet/home_my_wallet_content.dart'; 6 | 7 | enum HomeTabItem { myWallet, myFuture, add } 8 | 9 | extension HomeTabItemExtension on HomeTabItem { 10 | String title(BuildContext context) { 11 | switch (this) { 12 | case HomeTabItem.myWallet: 13 | return AppLoc.of(context).homeTabItemWalletTitle; 14 | case HomeTabItem.myFuture: 15 | return AppLoc.of(context).homeTabItemFutureTitle; 16 | case HomeTabItem.add: 17 | return AppLoc.of(context).homeTabItemAddTitle; 18 | } 19 | } 20 | 21 | Widget content(BuildContext context) { 22 | switch (this) { 23 | case HomeTabItem.myWallet: 24 | return const HomeMyWalletContent(); 25 | case HomeTabItem.myFuture: 26 | return const HomeMyFutureContent(); 27 | case HomeTabItem.add: 28 | return const HomeAddContent(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/ui/pages/home/my_future/widgets/home_my_future_assets_header.dart: -------------------------------------------------------------------------------- 1 | part of '../home_my_future_content.dart'; 2 | 3 | class _HomeMyFutureAssetsHeader extends StatelessWidget { 4 | final double? assetChange; 5 | final double? assetPercentageChange; 6 | final DateTime? date; 7 | final double? goal; 8 | final int? goalProgress; 9 | 10 | const _HomeMyFutureAssetsHeader({ 11 | required this.assetChange, 12 | required this.assetPercentageChange, 13 | required this.date, 14 | required this.goal, 15 | required this.goalProgress, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Container( 21 | decoration: AppDecorations.defaultBorder(), 22 | padding: EdgeInsets.all(AppDimensions.padding.defaultValue), 23 | child: Column( 24 | children: [ 25 | AssetPerformanceItem( 26 | assetChange: assetChange, 27 | assetPercentageChange: assetPercentageChange, 28 | title: AppLoc.of(context).homeMyFutureAssetPerformanceTitle, 29 | content: DateFormatterUtil.instance.formatDay( 30 | date: date, 31 | ), 32 | ), 33 | SizedBox( 34 | height: AppDimensions.padding.bigValue, 35 | ), 36 | ProgressBar( 37 | currentProgress: goalProgress ?? 0, 38 | goal: goal != null 39 | ? CurrencyFormatterUtil.instance.format( 40 | value: goal!, 41 | ) 42 | : null, 43 | summary: PercentageFormatterUtil.instance.format( 44 | value: goalProgress?.toDouble() ?? 0, 45 | decimalDigits: 0, 46 | ), 47 | ), 48 | ], 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/ui/pages/home/my_future/widgets/home_my_future_retirement_chart.dart: -------------------------------------------------------------------------------- 1 | part of '../home_my_future_content.dart'; 2 | 3 | class _HomeMyFutureRetirementChart extends StatelessWidget { 4 | final RetirementPlanModel? data; 5 | final String timeInterval; 6 | final ChartTab selectedTab; 7 | final Function(ChartTab tab) onTabSelected; 8 | 9 | const _HomeMyFutureRetirementChart({ 10 | required this.data, 11 | required this.timeInterval, 12 | required this.selectedTab, 13 | required this.onTabSelected, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return ChartContainer( 19 | title: AppLoc.of(context).homeMyFutureChartTitle, 20 | content: timeInterval, 21 | selectedTab: selectedTab, 22 | onTabSelected: onTabSelected, 23 | chart: data != null 24 | ? _buildChart( 25 | context, 26 | data: data!, 27 | ) 28 | : const SizedBox.shrink(), 29 | ); 30 | } 31 | 32 | Widget _buildChart( 33 | BuildContext context, { 34 | required RetirementPlanModel data, 35 | }) { 36 | return Chart( 37 | layers: ChartFactory.fromRetirementPlanModel( 38 | data, 39 | daysTo: selectedTab.days(), 40 | ), 41 | padding: const EdgeInsets.only( 42 | bottom: 8.0, 43 | left: 20.0, 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/ui/pages/home/my_wallet/widgets/home_my_wallet_goals_section.dart: -------------------------------------------------------------------------------- 1 | part of '../home_my_wallet_content.dart'; 2 | 3 | class _HomeMyWalletGoalsSection extends StatelessWidget { 4 | final List goals; 5 | final VoidCallback onMorePressed; 6 | 7 | const _HomeMyWalletGoalsSection({ 8 | required this.goals, 9 | required this.onMorePressed, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Column( 15 | children: [ 16 | SectionHeader( 17 | title: AppLoc.of(context).homeMyWalletSummaryGoalsSectionTitle, 18 | onMorePressed: onMorePressed, 19 | ), 20 | _buildGoalsList(context) 21 | ], 22 | ); 23 | } 24 | 25 | Widget _buildGoalsList(BuildContext context) { 26 | return Row( 27 | crossAxisAlignment: CrossAxisAlignment.start, 28 | children: goals 29 | .getRange(0, 2) 30 | .map( 31 | (goal) => Expanded( 32 | child: _HomeMyWalletGoalCell( 33 | title: goal.name, 34 | goal: goal.goal, 35 | change: goal.change, 36 | reached: goal.reached, 37 | onPressed: () => 38 | AdaptiveAlertDialogFactory.showContentUnavailable(context), 39 | ), 40 | ), 41 | ) 42 | .toList() 43 | ..insert( 44 | 1, 45 | SizedBox( 46 | width: AppDimensions.padding.defaultValue, 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/ui/pages/offers/details/widgets/offer_details_header.dart: -------------------------------------------------------------------------------- 1 | part of '../offer_details_page.dart'; 2 | 3 | class _OfferDetailsHeader extends StatelessWidget { 4 | final String leftTitle; 5 | final String leftContent; 6 | final String rightTitle; 7 | final String rightContent; 8 | 9 | const _OfferDetailsHeader({ 10 | required this.leftTitle, 11 | required this.leftContent, 12 | required this.rightTitle, 13 | required this.rightContent, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return _OfferDetailsSummary( 19 | left: Column( 20 | // TODO (AK) add logic 21 | children: [ 22 | Text( 23 | leftTitle, 24 | style: AppTextStyles.h4(), 25 | ), 26 | Text( 27 | leftContent, 28 | style: AppTextStyles.caption2Bold().copyWith( 29 | color: AppColors.navy.withOpacity(0.6), 30 | ), 31 | ), 32 | ], 33 | ), 34 | right: Column( 35 | children: [ 36 | Text( 37 | rightTitle, 38 | style: AppTextStyles.h4(), 39 | ), 40 | Text( 41 | rightContent, 42 | style: AppTextStyles.caption2Bold().copyWith( 43 | color: AppColors.success100, 44 | ), 45 | ), 46 | ], 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/ui/pages/offers/details/widgets/offer_details_summary.dart: -------------------------------------------------------------------------------- 1 | part of '../offer_details_page.dart'; 2 | 3 | class _OfferDetailsSummary extends StatelessWidget { 4 | final Widget left; 5 | final Widget right; 6 | 7 | const _OfferDetailsSummary({ 8 | required this.left, 9 | required this.right, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return IntrinsicHeight( 15 | child: DecoratedBox( 16 | decoration: AppDecorations.defaultBorder(), 17 | child: Row( 18 | children: [ 19 | Expanded( 20 | child: Padding( 21 | padding: const EdgeInsets.all(16.0), 22 | child: Center( 23 | child: left, 24 | ), 25 | ), 26 | ), 27 | Container( 28 | width: 1.0, 29 | color: AppColors.gray400, 30 | ), 31 | Expanded( 32 | child: Padding( 33 | padding: const EdgeInsets.all(16.0), 34 | child: Center( 35 | child: right, 36 | ), 37 | ), 38 | ), 39 | ], 40 | ), 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/ui/pages/onboarding/cubit/onboarding_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:vizier/data/storages/common_storage.dart'; 5 | import 'package:vizier/ui/pages/onboarding/onboarding_item.dart'; 6 | 7 | part 'onboarding_state.dart'; 8 | 9 | part 'onboarding_cubit.freezed.dart'; 10 | 11 | @injectable 12 | class OnboardingCubit extends Cubit { 13 | CommonStorage storage; 14 | 15 | OnboardingCubit(this.storage) : super(OnboardingState.initial()); 16 | 17 | void saveFirstTimeOpened({required bool isOpened}) { 18 | storage.storeIsFirstLaunch(isFirstLaunch: isOpened); 19 | emit( 20 | state.copyWith( 21 | firstTimeOpened: isOpened, 22 | ), 23 | ); 24 | } 25 | 26 | void changePage(int currentPage) { 27 | emit( 28 | state.copyWith( 29 | currentPage: currentPage, 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/ui/pages/onboarding/cubit/onboarding_state.dart: -------------------------------------------------------------------------------- 1 | part of 'onboarding_cubit.dart'; 2 | 3 | @freezed 4 | class OnboardingState with _$OnboardingState { 5 | factory OnboardingState({ 6 | required bool firstTimeOpened, 7 | required int currentPage, 8 | }) = _OnboardingState; 9 | 10 | const OnboardingState._(); 11 | 12 | factory OnboardingState.initial() => OnboardingState( 13 | firstTimeOpened: true, 14 | currentPage: 0, 15 | ); 16 | 17 | bool get isLastPage => OnboardingItem.values.length == (currentPage + 1); 18 | } 19 | -------------------------------------------------------------------------------- /lib/ui/pages/onboarding/widgets/onboarding_item_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:vizier/config/styles/colors/app_colors.dart'; 4 | import 'package:vizier/config/styles/dimensions/app_dimensions.dart'; 5 | 6 | class OnboardingItemContent extends StatelessWidget { 7 | final int pageIndex; 8 | final String title; 9 | final String description; 10 | final String imageAsset; 11 | 12 | const OnboardingItemContent({ 13 | required this.pageIndex, 14 | required this.title, 15 | required this.description, 16 | required this.imageAsset, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Column( 22 | children: [ 23 | const Spacer(), 24 | _buildTitle(context), 25 | SizedBox( 26 | height: AppDimensions.padding.defaultValue, 27 | ), 28 | _buildDescription(context), 29 | const Spacer(), 30 | SvgPicture.asset(imageAsset), 31 | ], 32 | ); 33 | } 34 | 35 | Widget _buildTitle(BuildContext context) { 36 | return Text( 37 | title, 38 | style: Theme.of(context).textTheme.headline2, 39 | textAlign: TextAlign.center, 40 | ); 41 | } 42 | 43 | Widget _buildDescription(BuildContext context) { 44 | return Text( 45 | description, 46 | style: Theme.of(context).textTheme.bodyText2?.copyWith( 47 | color: AppColors.gray200, 48 | ), 49 | textAlign: TextAlign.center, 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/ui/pages/portfolio/widgets/portfolio_my_watchlist_section.dart: -------------------------------------------------------------------------------- 1 | part of '../portfolio_page.dart'; 2 | 3 | class _PortfolioMyWatchlistSection extends StatelessWidget { 4 | final List? companyAssets; 5 | final void Function(CompanyAssetModel) onCompanyPressed; 6 | final VoidCallback onMorePressed; 7 | 8 | const _PortfolioMyWatchlistSection({ 9 | required this.companyAssets, 10 | required this.onCompanyPressed, 11 | required this.onMorePressed, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Column( 17 | children: [ 18 | SectionHeader( 19 | title: AppLoc.of(context).portfolioMyWatchlistSectionTitle, 20 | onMorePressed: onMorePressed, 21 | ), 22 | if (companyAssets?.isNotEmpty ?? false) 23 | Container( 24 | clipBehavior: Clip.antiAlias, 25 | decoration: AppDecorations.defaultBorder(), 26 | child: _buildCompanyAssetsList(), 27 | ), 28 | ], 29 | ); 30 | } 31 | 32 | Widget _buildCompanyAssetsList() { 33 | return ListView.separated( 34 | itemBuilder: (context, index) { 35 | final CompanyAssetModel companyAsset = companyAssets![index]; 36 | return PortfolioMyWatchlistCell( 37 | companyAsset: companyAsset, 38 | onPressed: onCompanyPressed, 39 | ); 40 | }, 41 | itemCount: companyAssets!.length, 42 | physics: const NeverScrollableScrollPhysics(), 43 | separatorBuilder: (_, __) => const Divider(), 44 | shrinkWrap: true, 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/ui/pages/profile/widgets/profile_action_button.dart: -------------------------------------------------------------------------------- 1 | part of '../profile_page.dart'; 2 | 3 | class _ProfileActionButton extends StatelessWidget { 4 | final String title; 5 | final VoidCallback onPressed; 6 | 7 | const _ProfileActionButton({ 8 | required this.title, 9 | required this.onPressed, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return AdaptiveButton( 15 | padding: EdgeInsets.symmetric( 16 | horizontal: AppDimensions.padding.defaultValue, 17 | vertical: 18.0, 18 | ), 19 | onPressed: onPressed, 20 | child: _buildContent( 21 | context, 22 | ), 23 | ); 24 | } 25 | 26 | Widget _buildContent(BuildContext context) { 27 | return Row( 28 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 29 | children: [ 30 | _buildTitle(context), 31 | const Icon( 32 | Icons.arrow_forward_ios, 33 | color: AppColors.navy, 34 | size: 12.0, 35 | ), 36 | ], 37 | ); 38 | } 39 | 40 | Widget _buildTitle(BuildContext context) { 41 | return Text( 42 | title, 43 | style: Theme.of(context).textTheme.headline6, 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/ui/pages/profile/widgets/profile_app_bar.dart: -------------------------------------------------------------------------------- 1 | part of '../profile_page.dart'; 2 | 3 | class _ProfileAppBar extends AdaptiveAppBar { 4 | _ProfileAppBar( 5 | super.context, { 6 | required VoidCallback onLogoutPressed, 7 | }) : super( 8 | actions: [ 9 | _buildLogoutButton( 10 | context, 11 | onPressed: onLogoutPressed, 12 | ) 13 | ], 14 | centerTitle: false, 15 | title: Text( 16 | AppLoc.of(context).profilePageTitle, 17 | ), 18 | ); 19 | 20 | static Widget _buildLogoutButton( 21 | BuildContext context, { 22 | required VoidCallback onPressed, 23 | }) { 24 | return AdaptiveButton( 25 | onPressed: onPressed, 26 | child: Text( 27 | AppLoc.of(context).profilePageLogoutButton, 28 | style: Theme.of(context).textTheme.headline6?.copyWith( 29 | color: AppColors.primary100, 30 | ), 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/ui/pages/profile/widgets/profile_header.dart: -------------------------------------------------------------------------------- 1 | part of '../profile_page.dart'; 2 | 3 | class _ProfileHeader extends StatelessWidget { 4 | final UserModel? user; 5 | 6 | const _ProfileHeader({ 7 | required this.user, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Column( 13 | children: [ 14 | if (user != null) ...[ 15 | _buildAvatar( 16 | avatar: user!.avatar, 17 | ), 18 | const SizedBox( 19 | height: 12.0, 20 | ), 21 | _buildFullName( 22 | context, 23 | fullName: user!.fullName, 24 | ), 25 | _buildEmail( 26 | context, 27 | email: user!.email, 28 | ), 29 | ] 30 | ], 31 | ); 32 | } 33 | 34 | Widget _buildAvatar({ 35 | required String avatar, 36 | }) { 37 | return CircleAvatar( 38 | backgroundColor: AppColors.white, 39 | backgroundImage: AssetImage(avatar), 40 | radius: 40.0, 41 | ); 42 | } 43 | 44 | Widget _buildFullName( 45 | BuildContext context, { 46 | required String fullName, 47 | }) { 48 | return Text( 49 | fullName, 50 | style: Theme.of(context).textTheme.headline5, 51 | textAlign: TextAlign.center, 52 | ); 53 | } 54 | 55 | Widget _buildEmail( 56 | BuildContext context, { 57 | required String email, 58 | }) { 59 | return Text( 60 | email, 61 | style: Theme.of(context).textTheme.bodyText2?.copyWith( 62 | color: AppColors.navy.withOpacity(0.5), 63 | ), 64 | textAlign: TextAlign.center, 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/ui/widgets/animated_progress_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/constants/app_constants.dart'; 3 | import 'package:vizier/ui/widgets/progress_bar.dart'; 4 | 5 | class AnimatedProgressBar extends StatefulWidget { 6 | // Value between 0 and 100 7 | final int currentProgress; 8 | final Duration? duration; 9 | final String? summary; 10 | 11 | const AnimatedProgressBar({ 12 | required this.currentProgress, 13 | this.duration, 14 | this.summary, 15 | super.key, 16 | }); 17 | 18 | @override 19 | State createState() => _AnimatedProgressBarState(); 20 | } 21 | 22 | class _AnimatedProgressBarState extends State 23 | with TickerProviderStateMixin { 24 | late final AnimationController animationController = AnimationController( 25 | duration: widget.duration ?? AppConstants.animation.defaultDuration, 26 | vsync: this, 27 | ); 28 | late final Animation animation; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | animation = IntTween( 34 | begin: 0, 35 | end: widget.currentProgress, 36 | ).animate(animationController); 37 | animationController.forward(); 38 | } 39 | 40 | @override 41 | void dispose() { 42 | animationController.dispose(); 43 | super.dispose(); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | return AnimatedBuilder( 49 | animation: animation, 50 | builder: (_, __) => ProgressBar( 51 | currentProgress: animation.value, 52 | summary: widget.summary, 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/ui/widgets/arrow_change_value.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/styles/colors/app_colors.dart'; 3 | import 'package:vizier/config/styles/text_styles/app_text_styles.dart'; 4 | 5 | class ArrowChangeValue extends StatelessWidget { 6 | final String change; 7 | final bool isNegative; 8 | final Color? changeColor; 9 | final TextStyle? changeStyle; 10 | final bool arrowOnRightSide; 11 | 12 | const ArrowChangeValue({ 13 | required this.change, 14 | required this.isNegative, 15 | this.changeColor, 16 | this.changeStyle, 17 | this.arrowOnRightSide = false, 18 | super.key, 19 | }); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Row( 24 | children: [ 25 | if (!arrowOnRightSide) _buildIcon(context), 26 | _buildTitle(context), 27 | if (arrowOnRightSide) _buildIcon(context), 28 | ], 29 | ); 30 | } 31 | 32 | Widget _buildIcon(BuildContext context) { 33 | return Icon( 34 | isNegative ? Icons.arrow_drop_down : Icons.arrow_drop_up, 35 | color: changeColor ?? AppColors.white, 36 | size: 20.0, 37 | ); 38 | } 39 | 40 | Widget _buildTitle(BuildContext context) { 41 | return Text( 42 | change, 43 | style: (changeStyle ?? AppTextStyles.caption1Bold()).copyWith( 44 | color: changeColor ?? AppColors.white, 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/ui/widgets/credit_card/animated_credit_card.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_svg/flutter_svg.dart'; 5 | import 'package:vizier/config/styles/colors/app_colors.dart'; 6 | import 'package:vizier/config/styles/dimensions/app_dimensions.dart'; 7 | import 'package:vizier/config/styles/images/app_images.dart'; 8 | import 'package:vizier/config/styles/text_styles/app_text_styles.dart'; 9 | import 'package:vizier/extensions/extensions.dart'; 10 | import 'package:vizier/l10n/app_loc.dart'; 11 | import 'package:vizier/ui/models/card_account_type.dart'; 12 | import 'package:vizier/ui/widgets/animated_flip.dart'; 13 | import 'package:vizier/ui/widgets/animated_text.dart'; 14 | 15 | part 'credit_card_back.dart'; 16 | part 'credit_card_container.dart'; 17 | part 'credit_card_cvv.dart'; 18 | part 'credit_card_expiry.dart'; 19 | part 'credit_card_front.dart'; 20 | part 'credit_card_number.dart'; 21 | 22 | class AnimatedCreditCard extends StatelessWidget { 23 | final String balance; 24 | final String cvv; 25 | final String expiry; 26 | final String logo; 27 | final String number; 28 | final CardAccountType type; 29 | 30 | const AnimatedCreditCard({ 31 | required this.expiry, 32 | required this.number, 33 | required this.type, 34 | this.balance = '', 35 | this.cvv = '', 36 | this.logo = '', 37 | super.key, 38 | }); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return AnimatedFlip( 43 | back: _CreditCardBack( 44 | cvv: cvv, 45 | ), 46 | front: _CreditCardFront( 47 | balance: balance, 48 | expiry: expiry, 49 | logo: logo, 50 | number: number, 51 | ), 52 | isFront: type == CardAccountType.front, 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/ui/widgets/credit_card/credit_card_back.dart: -------------------------------------------------------------------------------- 1 | part of 'animated_credit_card.dart'; 2 | 3 | class _CreditCardBack extends StatelessWidget { 4 | final String cvv; 5 | 6 | const _CreditCardBack({ 7 | required this.cvv, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return _CreditCardContainer( 13 | child: Column( 14 | crossAxisAlignment: CrossAxisAlignment.stretch, 15 | children: [ 16 | Expanded( 17 | child: _buildTop(), 18 | ), 19 | _buildCvv(), 20 | const Spacer(), 21 | ], 22 | ), 23 | ); 24 | } 25 | 26 | Widget _buildTop() { 27 | return Column( 28 | children: [ 29 | const Spacer(), 30 | Container( 31 | color: AppColors.black, 32 | height: 40.0, 33 | ), 34 | const Spacer(), 35 | ], 36 | ); 37 | } 38 | 39 | Widget _buildCvv() { 40 | return Padding( 41 | padding: AppDimensions.padding.defaultHorizontal(), 42 | child: Row( 43 | children: [ 44 | Expanded( 45 | child: Container( 46 | color: AppColors.white, 47 | height: 40.0, 48 | ), 49 | ), 50 | const SizedBox( 51 | width: 28.0, 52 | ), 53 | _CreditCardCvv( 54 | cvv: cvv, 55 | ), 56 | ], 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/ui/widgets/credit_card/credit_card_container.dart: -------------------------------------------------------------------------------- 1 | part of 'animated_credit_card.dart'; 2 | 3 | class _CreditCardContainer extends StatelessWidget { 4 | final Widget child; 5 | 6 | const _CreditCardContainer({ 7 | required this.child, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return AspectRatio( 13 | aspectRatio: 1.75, 14 | child: DecoratedBox( 15 | decoration: const BoxDecoration( 16 | borderRadius: BorderRadius.all(Radius.circular(12.0)), 17 | color: AppColors.primary100, 18 | ), 19 | child: child, 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/ui/widgets/credit_card/credit_card_cvv.dart: -------------------------------------------------------------------------------- 1 | part of 'animated_credit_card.dart'; 2 | 3 | class _CreditCardCvv extends StatelessWidget { 4 | final String cvv; 5 | 6 | const _CreditCardCvv({ 7 | required this.cvv, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final String mask = cvv.mask('***'); 13 | return Row( 14 | children: List.generate( 15 | mask.length, 16 | (index) => _buildLetter(mask[index]), 17 | ), 18 | ); 19 | } 20 | 21 | Widget _buildLetter(String text) { 22 | return SizedBox( 23 | width: 16.0, 24 | child: AnimatedText( 25 | text: text, 26 | style: AppTextStyles.caption1Bold().copyWith( 27 | color: AppColors.white, 28 | ), 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/ui/widgets/credit_card/credit_card_expiry.dart: -------------------------------------------------------------------------------- 1 | part of 'animated_credit_card.dart'; 2 | 3 | class _CreditCardExpiry extends StatelessWidget { 4 | final String expiry; 5 | 6 | const _CreditCardExpiry({ 7 | required this.expiry, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final String mask = expiry.mask('**/**'); 13 | return Row( 14 | children: List.generate( 15 | mask.length, 16 | (index) => _buildLetter(mask[index]), 17 | ), 18 | ); 19 | } 20 | 21 | Widget _buildLetter(String text) { 22 | return SizedBox( 23 | width: 10.0, 24 | child: AnimatedText( 25 | text: text, 26 | style: AppTextStyles.caption2().copyWith( 27 | color: AppColors.white, 28 | ), 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/ui/widgets/credit_card/credit_card_number.dart: -------------------------------------------------------------------------------- 1 | part of 'animated_credit_card.dart'; 2 | 3 | class _CreditCardNumber extends StatelessWidget { 4 | final String number; 5 | 6 | const _CreditCardNumber({ 7 | required this.number, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final String mask = number.mask('**** **** **** ****'); 13 | return Row( 14 | children: List.generate( 15 | mask.length, 16 | (index) => Expanded( 17 | child: FittedBox( 18 | child: _buildLetter(mask[index]), 19 | ), 20 | ), 21 | ), 22 | ); 23 | } 24 | 25 | Widget _buildLetter(String text) { 26 | return AnimatedText( 27 | text: text, 28 | style: AppTextStyles.h3().copyWith( 29 | color: AppColors.white, 30 | fontWeight: FontWeight.w500, 31 | fontFeatures: [ 32 | const FontFeature.tabularFigures(), 33 | ], 34 | shadows: [ 35 | Shadow( 36 | color: AppColors.black.withOpacity(0.2), 37 | offset: const Offset(0.0, 2.0), 38 | ), 39 | ], 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/ui/widgets/legend/models/legend.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | part 'legend_item.dart'; 4 | part 'legend_settings.dart'; 5 | 6 | class Legend { 7 | final List items; 8 | final LegendSettings settings; 9 | 10 | const Legend({ 11 | required this.items, 12 | required this.settings, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /lib/ui/widgets/legend/models/legend_item.dart: -------------------------------------------------------------------------------- 1 | part of 'legend.dart'; 2 | 3 | class LegendItem { 4 | final Color color; 5 | final String label; 6 | 7 | const LegendItem({ 8 | required this.color, 9 | required this.label, 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /lib/ui/widgets/legend/models/legend_settings.dart: -------------------------------------------------------------------------------- 1 | part of 'legend.dart'; 2 | 3 | class LegendSettings { 4 | final EdgeInsets padding; 5 | final double paddingBetweenItems; 6 | final TextStyle textStyle; 7 | final double sizeIcon; 8 | 9 | const LegendSettings({ 10 | required this.padding, 11 | required this.paddingBetweenItems, 12 | required this.sizeIcon, 13 | required this.textStyle, 14 | }); 15 | 16 | const LegendSettings.fromTextStyle(this.textStyle) 17 | : padding = EdgeInsets.zero, 18 | paddingBetweenItems = 8.0, 19 | sizeIcon = 8.0; 20 | } 21 | -------------------------------------------------------------------------------- /lib/ui/widgets/performance_percentage_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/styles/colors/app_colors.dart'; 3 | import 'package:vizier/ui/widgets/arrow_change_value.dart'; 4 | import 'package:vizier/utils/percentage_formatter_util.dart'; 5 | 6 | class PerformancePercentageBox extends StatelessWidget { 7 | final double value; 8 | 9 | const PerformancePercentageBox({ 10 | required this.value, 11 | super.key, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Container( 17 | decoration: BoxDecoration( 18 | borderRadius: BorderRadius.circular(4.0), 19 | color: value.isNegative ? AppColors.error100 : AppColors.success100, 20 | ), 21 | padding: const EdgeInsets.all(4.0), 22 | child: ArrowChangeValue( 23 | change: PercentageFormatterUtil.instance.format( 24 | value: value, 25 | ), 26 | isNegative: value.isNegative, 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/ui/widgets/ripple_remover.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class RippleRemover extends StatelessWidget { 6 | final Widget child; 7 | final bool isActive; 8 | 9 | RippleRemover({ 10 | required this.child, 11 | }) : isActive = Platform.isIOS; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | if (isActive) { 16 | return Theme( 17 | data: Theme.of(context).copyWith( 18 | highlightColor: Colors.transparent, 19 | splashColor: Colors.transparent, 20 | ), 21 | child: child, 22 | ); 23 | } else { 24 | return child; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/ui/widgets/section_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:vizier/config/styles/colors/app_colors.dart'; 3 | import 'package:vizier/config/styles/dimensions/app_dimensions.dart'; 4 | import 'package:vizier/ui/widgets/adaptive/adaptive_button.dart'; 5 | 6 | class SectionHeader extends StatelessWidget { 7 | final String title; 8 | final VoidCallback? onMorePressed; 9 | 10 | const SectionHeader({ 11 | required this.title, 12 | this.onMorePressed, 13 | super.key, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Row( 19 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 20 | children: [ 21 | _buildTitle(context), 22 | if (onMorePressed != null) _buildMoreButton(context), 23 | ], 24 | ); 25 | } 26 | 27 | Widget _buildTitle(BuildContext context) { 28 | return Text( 29 | title, 30 | style: Theme.of(context).textTheme.headline6, 31 | ); 32 | } 33 | 34 | Widget _buildMoreButton(BuildContext context) { 35 | return Transform.translate( 36 | offset: Offset(AppDimensions.padding.defaultValue, 0.0), 37 | child: AdaptiveButton( 38 | decoration: const BoxDecoration( 39 | shape: BoxShape.circle, 40 | ), 41 | padding: EdgeInsets.all( 42 | AppDimensions.padding.defaultValue, 43 | ), 44 | onPressed: onMorePressed, 45 | child: const Icon( 46 | Icons.more_horiz, 47 | color: AppColors.black, 48 | ), 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/ui/widgets/unfocus_keyboard_outside.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class UnfocusKeyboardOutside extends StatelessWidget { 4 | final Widget child; 5 | 6 | const UnfocusKeyboardOutside({ 7 | required this.child, 8 | super.key, 9 | }); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return GestureDetector( 14 | onTap: () => FocusScope.of(context).unfocus(), 15 | child: child, 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/utils/adaptive_widget_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | enum Platform { material, cupertino, adaptive } 4 | enum AdaptiveWidgetType { material, cupertino } 5 | 6 | class AdaptiveWidgetUtil { 7 | const AdaptiveWidgetUtil._(); 8 | 9 | static AdaptiveWidgetType getWidgetTypeOf( 10 | BuildContext context, { 11 | required Platform platform, 12 | }) { 13 | switch (platform) { 14 | case Platform.adaptive: 15 | final ThemeData theme = Theme.of(context); 16 | switch (theme.platform) { 17 | case TargetPlatform.iOS: 18 | return AdaptiveWidgetType.cupertino; 19 | case TargetPlatform.android: 20 | case TargetPlatform.fuchsia: 21 | default: 22 | return AdaptiveWidgetType.material; 23 | } 24 | case Platform.cupertino: 25 | return AdaptiveWidgetType.cupertino; 26 | case Platform.material: 27 | default: 28 | return AdaptiveWidgetType.material; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/utils/currency_formatter_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'package:vizier/config/injector/di.dart'; 5 | import 'package:vizier/config/styles/colors/app_colors.dart'; 6 | 7 | @singleton 8 | class CurrencyFormatterUtil { 9 | static CurrencyFormatterUtil get instance => DI.resolve(); 10 | 11 | final NumberFormat numberFormat = NumberFormat.currency( 12 | symbol: '€', 13 | ); 14 | 15 | String format({ 16 | required double value, 17 | }) { 18 | return numberFormat.format(value); 19 | } 20 | 21 | String formatWithChangePrefix({ 22 | required double value, 23 | }) { 24 | if (value.isNegative) { 25 | return format( 26 | value: value, 27 | ); 28 | } 29 | return '+ ${format(value: value)}'; 30 | } 31 | 32 | Color changeColor({ 33 | required double value, 34 | }) { 35 | return value.isNegative ? AppColors.error300 : AppColors.success100; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/utils/dummy_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:injectable/injectable.dart'; 4 | 5 | @injectable 6 | class DummyUtil { 7 | final Random _random = Random(); 8 | 9 | static const String _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; 10 | 11 | int randomInt({ 12 | int minNumber = 0, 13 | int maxNumber = 10, 14 | }) => 15 | _random.nextInt(maxNumber - minNumber) + minNumber; 16 | 17 | double randomDouble({ 18 | int minNumber = 0, 19 | int maxNumber = 10, 20 | }) => 21 | randomInt(minNumber: minNumber, maxNumber: maxNumber) + _random.nextDouble(); 22 | 23 | String randomString({ 24 | int length = 64, 25 | }) => 26 | String.fromCharCodes( 27 | Iterable.generate( 28 | length, 29 | (_) => _chars.codeUnitAt( 30 | _random.nextInt(_chars.length), 31 | ), 32 | ), 33 | ); 34 | 35 | bool randomBool() => _random.nextBool(); 36 | } 37 | -------------------------------------------------------------------------------- /lib/utils/percentage_formatter_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:intl/intl.dart'; 3 | import 'package:vizier/config/injector/di.dart'; 4 | 5 | @singleton 6 | class PercentageFormatterUtil { 7 | static PercentageFormatterUtil get instance => DI.resolve(); 8 | 9 | final NumberFormat numberFormat = NumberFormat.decimalPercentPattern( 10 | decimalDigits: 2, 11 | ); 12 | 13 | String format({ 14 | required double value, 15 | int decimalDigits = 2, 16 | }) { 17 | if (decimalDigits != 2) { 18 | return NumberFormat.decimalPercentPattern( 19 | decimalDigits: decimalDigits, 20 | ).format(value / 100.0); 21 | } 22 | return numberFormat.format(value.abs() / 100.0); 23 | } 24 | 25 | String formatWithChangePrefix({ 26 | required double value, 27 | }) { 28 | if (value.isNegative) { 29 | return format( 30 | value: value, 31 | ); 32 | } 33 | return '+ ${format(value: value)}'; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/utils/validation/validators_util.dart: -------------------------------------------------------------------------------- 1 | // ignore: avoid_classes_with_only_static_members 2 | abstract class ValidatorsUtil { 3 | static final RegExp _emailRegExp = RegExp( 4 | r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', 5 | ); 6 | 7 | static bool isValidEmail(String? email) { 8 | return email != null && _emailRegExp.hasMatch(email); 9 | } 10 | 11 | static bool isNotEmpty(String text) { 12 | return text.isNotEmpty; 13 | } 14 | 15 | static bool isNotBlank(String? text) { 16 | if (text != null) { 17 | return isNotEmpty(text.trim()); 18 | } else { 19 | return false; 20 | } 21 | } 22 | 23 | static bool isNotLongerThan(String text, int maxCharacters) { 24 | return text.length <= maxCharacters; 25 | } 26 | 27 | static bool isNotLessThan(String text, int minCharacters) { 28 | return text.length >= minCharacters; 29 | } 30 | 31 | static bool isGreaterThan(String text, int number) { 32 | final int? intValue = int.tryParse(text); 33 | if (intValue != null) { 34 | return intValue > number; 35 | } 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: vizier 2 | description: A new Flutter project. 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.17.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | #Localizations 13 | flutter_localizations: 14 | sdk: flutter 15 | intl: ^0.17.0 16 | #Bloc 17 | bloc: ^8.1.0 18 | flutter_bloc: ^8.1.1 19 | #Code generation 20 | freezed_annotation: ^2.1.0 21 | json_annotation: ^4.6.0 22 | #Dependency Injection 23 | injectable: ^1.5.3 24 | get_it: ^7.2.0 25 | #Storages 26 | flutter_secure_storage: ^6.0.0 27 | shared_preferences: ^2.0.15 28 | #Routing 29 | auto_route: ^5.0.1 30 | #UI 31 | flutter_svg: ^1.1.4 32 | mrx_charts: ^0.1.3 33 | 34 | dev_dependencies: 35 | auto_route_generator: ^5.0.2 36 | build_runner: ^2.2.0 37 | flutter_lints: ^2.0.1 38 | freezed: ^2.1.0+1 39 | injectable_generator: ^1.5.4 40 | json_serializable: ^6.3.1 41 | lint: ^1.10.0 42 | 43 | flutter: 44 | uses-material-design: true 45 | generate: true 46 | assets: 47 | - assets/svgs/ 48 | - assets/mocks/finances/ 49 | - assets/mocks/user/ 50 | - assets/mocks/portfolio/ 51 | - assets/mocks/transactions/ 52 | fonts: 53 | - family: Ubuntu 54 | fonts: 55 | - asset: assets/fonts/ubuntu/Ubuntu-Light.ttf 56 | weight: 300 57 | - asset: assets/fonts/ubuntu/Ubuntu-Regular.ttf 58 | weight: 400 59 | - asset: assets/fonts/ubuntu/Ubuntu-Medium.ttf 60 | weight: 500 61 | - asset: assets/fonts/ubuntu/Ubuntu-Bold.ttf 62 | weight: 700 63 | --------------------------------------------------------------------------------