├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── feature_request.yaml └── workflows │ ├── build-fat-apk-android-beta.yaml │ ├── deploy-android-build.yaml │ ├── deploy-ios-builds.yaml │ └── test.yaml ├── .gitignore ├── .idx └── dev.nix ├── .markdownlint.json ├── .metadata ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RELEASE_TEMPLATE.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── mn │ │ │ │ └── flow │ │ │ │ └── flow │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── monochrome.png │ │ │ └── notification_default.png │ │ │ ├── drawable-mdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── monochrome.png │ │ │ └── notification_default.png │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── monochrome.png │ │ │ └── notification_default.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── monochrome.png │ │ │ └── notification_default.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_launcher_background.png │ │ │ ├── ic_launcher_foreground.png │ │ │ ├── monochrome.png │ │ │ └── notification_default.png │ │ │ ├── drawable │ │ │ ├── launch_background.xml │ │ │ └── notification_default.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── raw │ │ │ └── keep.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── proguard-rules.pro └── settings.gradle ├── assets ├── fonts │ ├── NotoEmoji-Regular.ttf │ ├── NotoSans-Regular.ttf │ ├── NotoSansArabic-Regular.ttf │ ├── NotoSansHebrew-Regular.ttf │ ├── OFL.txt │ ├── Poppins-Bold.ttf │ ├── Poppins-Italic.ttf │ ├── Poppins-Medium.ttf │ ├── Poppins-Regular.ttf │ └── Poppins-SemiBold.ttf ├── images │ ├── 2.0x │ │ ├── flow.png │ │ ├── map_square.png │ │ └── pin.png │ ├── 3.0x │ │ ├── flow.png │ │ └── pin.png │ ├── 4.0x │ │ └── flow.png │ ├── external │ │ └── ivy_wallet.png │ ├── flow.png │ ├── map_square.png │ ├── missing.png │ ├── missing.svg │ ├── pin.png │ └── pin.svg └── l10n │ ├── ar.json │ ├── de_DE.json │ ├── en.json │ ├── es_ES.json │ ├── fr_FR.json │ ├── it_IT.json │ ├── mn_MN.json │ ├── ru_RU.json │ └── tr_TR.json ├── devtools_options.yaml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.pbxproj.tmp │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── LauncherIcons │ │ ├── blissfulBerry-ipad-pro@2x.png │ │ ├── blissfulBerry-ipad@2x.png │ │ ├── blissfulBerry@2x.png │ │ ├── blissfulBerry@3x.png │ │ ├── bohemianBlue-ipad-pro@2x.png │ │ ├── bohemianBlue-ipad@2x.png │ │ ├── bohemianBlue@2x.png │ │ ├── bohemianBlue@3x.png │ │ ├── burntSienna-ipad-pro@2x.png │ │ ├── burntSienna-ipad@2x.png │ │ ├── burntSienna@2x.png │ │ ├── burntSienna@3x.png │ │ ├── cherryPlum-ipad-pro@2x.png │ │ ├── cherryPlum-ipad@2x.png │ │ ├── cherryPlum@2x.png │ │ ├── cherryPlum@3x.png │ │ ├── crispChristmasCranberries-ipad-pro@2x.png │ │ ├── crispChristmasCranberries-ipad@2x.png │ │ ├── crispChristmasCranberries@2x.png │ │ ├── crispChristmasCranberries@3x.png │ │ ├── egyptianBlue-ipad-pro@2x.png │ │ ├── egyptianBlue-ipad@2x.png │ │ ├── egyptianBlue@2x.png │ │ ├── egyptianBlue@3x.png │ │ ├── flagGreen-ipad-pro@2x.png │ │ ├── flagGreen-ipad@2x.png │ │ ├── flagGreen@2x.png │ │ ├── flagGreen@3x.png │ │ ├── hydraTurquoise-ipad-pro@2x.png │ │ ├── hydraTurquoise-ipad@2x.png │ │ ├── hydraTurquoise@2x.png │ │ ├── hydraTurquoise@3x.png │ │ ├── peacockBlue-ipad-pro@2x.png │ │ ├── peacockBlue-ipad@2x.png │ │ ├── peacockBlue@2x.png │ │ ├── peacockBlue@3x.png │ │ ├── shadeOfViolet-ipad-pro@2x.png │ │ ├── shadeOfViolet-ipad@2x.png │ │ ├── shadeOfViolet@2x.png │ │ ├── shadeOfViolet@3x.png │ │ ├── soilOfAvagddu-ipad-pro@2x.png │ │ ├── soilOfAvagddu-ipad@2x.png │ │ ├── soilOfAvagddu@2x.png │ │ ├── soilOfAvagddu@3x.png │ │ ├── spaceBattleBlue-ipad-pro@2x.png │ │ ├── spaceBattleBlue-ipad@2x.png │ │ ├── spaceBattleBlue@2x.png │ │ ├── spaceBattleBlue@3x.png │ │ ├── spreadsheetGreen-ipad-pro@2x.png │ │ ├── spreadsheetGreen-ipad@2x.png │ │ ├── spreadsheetGreen@2x.png │ │ ├── spreadsheetGreen@3x.png │ │ ├── tokiwaGreen-ipad-pro@2x.png │ │ ├── tokiwaGreen-ipad@2x.png │ │ ├── tokiwaGreen@2x.png │ │ ├── tokiwaGreen@3x.png │ │ ├── toyCamouflage-ipad-pro@2x.png │ │ ├── toyCamouflage-ipad@2x.png │ │ ├── toyCamouflage@2x.png │ │ ├── toyCamouflage@3x.png │ │ ├── tropicana-ipad-pro@2x.png │ │ ├── tropicana-ipad@2x.png │ │ ├── tropicana@2x.png │ │ └── tropicana@3x.png │ ├── Runner-Bridging-Header.h │ └── Runner.entitlements └── RunnerTests │ └── RunnerTests.swift ├── launcher_icons ├── adaptive_background.png ├── adaptive_foreground.png ├── defaults-light │ ├── blissfulBerry-ipad-pro@2x.png │ ├── blissfulBerry-ipad@2x.png │ ├── blissfulBerry@2x.png │ ├── blissfulBerry@3x.png │ ├── bohemianBlue-ipad-pro@2x.png │ ├── bohemianBlue-ipad@2x.png │ ├── bohemianBlue@2x.png │ ├── bohemianBlue@3x.png │ ├── burntSienna-ipad-pro@2x.png │ ├── burntSienna-ipad@2x.png │ ├── burntSienna@2x.png │ ├── burntSienna@3x.png │ ├── cherryPlum-ipad-pro@2x.png │ ├── cherryPlum-ipad@2x.png │ ├── cherryPlum@2x.png │ ├── cherryPlum@3x.png │ ├── crispChristmasCranberries-ipad-pro@2x.png │ ├── crispChristmasCranberries-ipad@2x.png │ ├── crispChristmasCranberries@2x.png │ ├── crispChristmasCranberries@3x.png │ ├── egyptianBlue-ipad-pro@2x.png │ ├── egyptianBlue-ipad@2x.png │ ├── egyptianBlue@2x.png │ ├── egyptianBlue@3x.png │ ├── flagGreen-ipad-pro@2x.png │ ├── flagGreen-ipad@2x.png │ ├── flagGreen@2x.png │ ├── flagGreen@3x.png │ ├── hydraTurquoise-ipad-pro@2x.png │ ├── hydraTurquoise-ipad@2x.png │ ├── hydraTurquoise@2x.png │ ├── hydraTurquoise@3x.png │ ├── peacockBlue-ipad-pro@2x.png │ ├── peacockBlue-ipad@2x.png │ ├── peacockBlue@2x.png │ ├── peacockBlue@3x.png │ ├── shadeOfViolet-ipad-pro@2x.png │ ├── shadeOfViolet-ipad@2x.png │ ├── shadeOfViolet@2x.png │ ├── shadeOfViolet@3x.png │ ├── soilOfAvagddu-ipad-pro@2x.png │ ├── soilOfAvagddu-ipad@2x.png │ ├── soilOfAvagddu@2x.png │ ├── soilOfAvagddu@3x.png │ ├── spaceBattleBlue-ipad-pro@2x.png │ ├── spaceBattleBlue-ipad@2x.png │ ├── spaceBattleBlue@2x.png │ ├── spaceBattleBlue@3x.png │ ├── spreadsheetGreen-ipad-pro@2x.png │ ├── spreadsheetGreen-ipad@2x.png │ ├── spreadsheetGreen@2x.png │ ├── spreadsheetGreen@3x.png │ ├── tokiwaGreen-ipad-pro@2x.png │ ├── tokiwaGreen-ipad@2x.png │ ├── tokiwaGreen@2x.png │ ├── tokiwaGreen@3x.png │ ├── toyCamouflage-ipad-pro@2x.png │ ├── toyCamouflage-ipad@2x.png │ ├── toyCamouflage@2x.png │ ├── toyCamouflage@3x.png │ ├── tropicana-ipad-pro@2x.png │ ├── tropicana-ipad@2x.png │ ├── tropicana@2x.png │ └── tropicana@3x.png ├── flow.png └── monochrome.png ├── lib ├── constants.dart ├── data │ ├── chart_data.dart │ ├── currencies.dart │ ├── exchange_rates.dart │ ├── exchange_rates_set.dart │ ├── flow_analytics.dart │ ├── flow_icon.dart │ ├── flow_notification_payload.dart │ ├── flow_notification_payload.g.dart │ ├── flow_standard_report.dart │ ├── github │ │ └── contributor.dart │ ├── icons.dart │ ├── internal_nofications │ │ └── internal_notification.dart │ ├── money.dart │ ├── multi_currency_flow.dart │ ├── prefs │ │ ├── frecency.dart │ │ ├── frecency.g.dart │ │ ├── frecency_group.dart │ │ └── frecency_group.g.dart │ ├── recurrence_mode.dart │ ├── setup │ │ ├── default_accounts.dart │ │ └── default_categories.dart │ ├── single_currency_flow.dart │ ├── transaction_filter.dart │ ├── transaction_filter.g.dart │ └── transactions_filter │ │ ├── group_range.dart │ │ ├── search_data.dart │ │ ├── search_data.g.dart │ │ ├── sort_field.dart │ │ └── time_range.dart ├── entity │ ├── _base.dart │ ├── account.dart │ ├── account.g.dart │ ├── backup_entry.dart │ ├── budget.dart │ ├── budget.g.dart │ ├── category.dart │ ├── category.g.dart │ ├── profile.dart │ ├── profile.g.dart │ ├── recurring_transaction.dart │ ├── recurring_transaction.g.dart │ ├── transaction.dart │ ├── transaction.g.dart │ ├── transaction │ │ ├── extensions │ │ │ ├── base.dart │ │ │ └── default │ │ │ │ ├── geo.dart │ │ │ │ ├── geo.g.dart │ │ │ │ ├── recurring.dart │ │ │ │ ├── recurring.g.dart │ │ │ │ ├── transfer.dart │ │ │ │ └── transfer.g.dart │ │ ├── subtype.dart │ │ ├── type.dart │ │ └── wrapper.dart │ ├── transaction_filter_preset.dart │ ├── transaction_filter_preset.g.dart │ ├── user_preferences.dart │ └── user_preferences.g.dart ├── form_validators.dart ├── graceful_migrations.dart ├── l10n │ ├── extensions.dart │ ├── flow_localizations.dart │ ├── localized_exception.dart │ ├── named_enum.dart │ └── supported_languages.dart ├── logging.dart ├── main.dart ├── objectbox.dart ├── objectbox │ ├── actions.dart │ ├── objectbox-model.json │ └── objectbox.g.dart ├── prefs │ ├── local_preferences.dart │ ├── pending_transactions.dart │ ├── theme.dart │ └── transitive.dart ├── providers │ ├── accounts_provider.dart │ └── categories.dart ├── reports │ ├── category_flow_report.dart │ ├── interval_flow_report.dart │ ├── range_forecast_report.dart │ ├── report.dart │ └── trends_report.dart ├── routes.dart ├── routes │ ├── account │ │ └── account_edit_page.dart │ ├── account_page.dart │ ├── accounts_page.dart │ ├── categories_page.dart │ ├── category │ │ └── category_edit_page.dart │ ├── category_page.dart │ ├── community │ │ └── contributors_page.dart │ ├── debug │ │ ├── debug_icloud_page.dart │ │ ├── debug_log_page.dart │ │ ├── debug_logs_page.dart │ │ ├── debug_scheduled_notifications_page.dart │ │ └── debug_theme_page.dart │ ├── error_page.dart │ ├── export │ │ ├── export_history_page.dart │ │ └── export_pdf_page.dart │ ├── export_options_page.dart │ ├── export_page.dart │ ├── home │ │ ├── accounts_tab.dart │ │ ├── home_tab.dart │ │ ├── profile_tab.dart │ │ └── stats_tab.dart │ ├── home_page.dart │ ├── import_page.dart │ ├── import_wizard │ │ ├── csv.dart │ │ ├── ivy.dart │ │ ├── v1.dart │ │ └── v2.dart │ ├── preferences │ │ ├── button_order_preferences_page.dart │ │ ├── language_selection_sheet.dart │ │ ├── money_formatting_preferences_page.dart │ │ ├── numpad_preferences_page.dart │ │ ├── pending_transactions_preferences_page.dart │ │ ├── reminders_preferences_page.dart │ │ ├── sections │ │ │ ├── haptics.dart │ │ │ ├── icloud.dart │ │ │ ├── lock_app.dart │ │ │ └── privacy.dart │ │ ├── sync_preferences_page.dart │ │ ├── theme_preferences_page.dart │ │ ├── transaction_geo_preferences_page.dart │ │ ├── transaction_list_item_appearance_preferences_page.dart │ │ ├── transfer_preferences_page.dart │ │ └── trash_bin_preferences_page.dart │ ├── preferences_page.dart │ ├── profile_page.dart │ ├── setup │ │ ├── setup_accounts_page.dart │ │ ├── setup_categories_page.dart │ │ ├── setup_currency_page.dart │ │ ├── setup_onboarding_page.dart │ │ ├── setup_profile_page.dart │ │ └── setup_profile_picture_page.dart │ ├── setup_page.dart │ ├── stats │ │ └── stats_by_group_page.dart │ ├── support_page.dart │ ├── transaction_page.dart │ ├── transaction_page │ │ ├── amount_text.dart │ │ ├── description_section.dart │ │ ├── input_amount_sheet.dart │ │ ├── input_amount_sheet │ │ │ ├── calculator_button.dart │ │ │ └── input_value.dart │ │ ├── section.dart │ │ ├── select_account_sheet.dart │ │ ├── select_category_sheet.dart │ │ ├── select_recurrence.dart │ │ ├── select_recurring_update_mode_sheet.dart │ │ └── title_input.dart │ ├── transactions_page.dart │ └── utils │ │ ├── crop_square_image_page.dart │ │ └── edit_markdown_page.dart ├── services │ ├── accounts.dart │ ├── connectivity.dart │ ├── exchange_rates.dart │ ├── github.dart │ ├── icloud_sync.dart │ ├── internal_notifications.dart │ ├── local_auth.dart │ ├── notifications.dart │ ├── recurring_transactions.dart │ ├── sync.dart │ ├── transactions.dart │ └── user_preferences.dart ├── sync │ ├── exception.dart │ ├── export.dart │ ├── export │ │ ├── export_csv.dart │ │ ├── export_csv │ │ │ └── header_v1.dart │ │ ├── export_pdf.dart │ │ ├── export_pdf │ │ │ └── headers.dart │ │ ├── export_v1.dart │ │ ├── export_v2.dart │ │ └── mode.dart │ ├── import.dart │ ├── import │ │ ├── base.dart │ │ ├── external │ │ │ └── ivy_wallet_csv.dart │ │ ├── import_csv.dart │ │ ├── import_v1.dart │ │ └── import_v2.dart │ ├── model │ │ ├── base.dart │ │ ├── csv │ │ │ ├── csv_parsed_transaction.dart │ │ │ ├── parsed_data.dart │ │ │ └── parsers.dart │ │ ├── external │ │ │ └── ivy │ │ │ │ ├── ivy_wallet_csv.dart │ │ │ │ ├── ivy_wallet_transaction.dart │ │ │ │ └── parsers.dart │ │ ├── model_v1.dart │ │ ├── model_v1.g.dart │ │ ├── model_v2.dart │ │ └── model_v2.g.dart │ └── sync.dart ├── theme │ ├── color_themes │ │ ├── catppuccin │ │ │ ├── frappe.dart │ │ │ ├── frappe.json │ │ │ ├── macchiato.dart │ │ │ ├── macchiato.json │ │ │ ├── mocha.dart │ │ │ └── mocha.json │ │ ├── flow │ │ │ ├── flow_darks.dart │ │ │ ├── flow_lights.dart │ │ │ └── flow_oleds.dart │ │ ├── palenight.dart │ │ └── registry.dart │ ├── flow_color_scheme.dart │ ├── flow_custom_colors.dart │ ├── flow_theme_group.dart │ ├── helpers.dart │ ├── names.dart │ ├── navbar_theme.dart │ ├── pie_theme_extension.dart │ ├── primary_colors.dart │ ├── text_theme.dart │ └── theme.dart ├── utils │ ├── csv_parser.dart │ ├── extensions.dart │ ├── extensions │ │ ├── backup_entry.dart │ │ ├── custom_popups.dart │ │ ├── directionality.dart │ │ ├── go_router.dart │ │ ├── interval_report.dart │ │ ├── iterables.dart │ │ ├── num.dart │ │ ├── recurring_transaction.dart │ │ ├── string.dart │ │ ├── toast.dart │ │ ├── transaction.dart │ │ └── transaction_filter.dart │ ├── guess_preset_icon.dart │ ├── is_desktop.dart │ ├── jasonable.dart │ ├── json │ │ ├── time_range_converter.dart │ │ └── utc_datetime_converter.dart │ ├── line_break_normalizer.dart │ ├── number_formatting.dart │ ├── open_url.dart │ ├── optional.dart │ ├── pick_file.dart │ ├── shortcut.dart │ ├── simple_query_sorter.dart │ └── utils.dart └── widgets │ ├── account │ └── update_balance_options_sheet.dart │ ├── account_card.dart │ ├── account_card_skeleton.dart │ ├── action_card.dart │ ├── add_category_card.dart │ ├── categories │ └── no_categories.dart │ ├── category │ └── transactions_info.dart │ ├── category_card.dart │ ├── chart_legend.dart │ ├── community │ └── contributors │ │ └── contributor_card.dart │ ├── default_transaction_filter_head.dart │ ├── delete_button.dart │ ├── export │ ├── export_history │ │ ├── backup_entry_card.dart │ │ └── no_backups.dart │ └── export_success.dart │ ├── flow_card.dart │ ├── general │ ├── blur_backgorund.dart │ ├── button.dart │ ├── context_menu.dart │ ├── directional_chevron.dart │ ├── directional_slidable.dart │ ├── flow_icon.dart │ ├── form_close_button.dart │ ├── frame.dart │ ├── info_text.dart │ ├── list_header.dart │ ├── map.dart │ ├── markdown_view.dart │ ├── modal_overflow_bar.dart │ ├── modal_sheet.dart │ ├── money_text.dart │ ├── money_text_builder.dart │ ├── money_text_raw.dart │ ├── pending_transactions_header.dart │ ├── profile_picture.dart │ ├── rtl_flipper.dart │ ├── spinner.dart │ ├── surface.dart │ ├── wavy_divider.dart │ └── wavy_divider │ │ └── wavy_divider_painter.dart │ ├── geo_permission_missing_reminder.dart │ ├── grouped_transactions_list_view.dart │ ├── home │ ├── greetings_bar.dart │ ├── home │ │ ├── account │ │ │ ├── no_accounts.dart │ │ │ └── total_balance.dart │ │ ├── flow_cards.dart │ │ ├── info_card.dart │ │ └── no_transactions.dart │ ├── navbar.dart │ ├── navbar │ │ ├── navbar_button.dart │ │ └── new_transaction_button.dart │ ├── preferences │ │ ├── button_order_preferences │ │ │ └── transaction_type_button.dart │ │ ├── numpad_preferences │ │ │ └── numpad_selector_radio.dart │ │ ├── profile_card.dart │ │ └── transfer_preferences │ │ │ ├── combine_transfer_radio.dart.dart │ │ │ └── demo_transaction_list_tile.dart │ ├── privacy_toggler.dart │ └── stats │ │ ├── group_pie_chart.dart │ │ ├── info_card_with_delta.dart │ │ ├── most_spending_category.dart │ │ ├── no_data.dart │ │ ├── pie_graph_view.dart │ │ ├── pie_percent_badge.dart │ │ └── range_daily_chart.dart │ ├── import │ └── file_select_area.dart │ ├── import_wizard │ ├── backup_info.dart │ ├── csv │ │ ├── account_currency_list_tile.dart │ │ └── backup_info_csv.dart │ ├── import_item_list_tile.dart │ ├── import_success.dart │ ├── ivy_wallet │ │ └── backup_info.dart │ ├── v1 │ │ └── backup_info_v1.dart │ └── v2 │ │ └── backup_info_v2.dart │ ├── internal_notifications │ ├── auto_backup_reminder.dart │ ├── internal_notification_list_tile.dart │ ├── internal_notification_section.dart │ ├── rate_app_notification.dart │ └── star_on_github_notification.dart │ ├── location_picker_sheet.dart │ ├── month_selector_sheet.dart │ ├── no_result.dart │ ├── notifications_permission_missing_reminder.dart │ ├── numpad.dart │ ├── numpad_button.dart │ ├── rates_missing_warning.dart │ ├── reports │ ├── category_report_view.dart │ └── interval_flow_report_view.dart │ ├── setup │ ├── accounts │ │ ├── account_preset_card.dart │ │ └── add_account_card.dart │ ├── categories │ │ └── category_preset_card.dart │ ├── foss_slide.dart │ ├── offline_slide.dart │ └── welcome_slide.dart │ ├── sheets │ ├── month_selector_sheet │ │ └── month_button.dart │ ├── recurrence │ │ ├── select_day_sheet.dart │ │ └── selectors │ │ │ └── weekday_selector.dart │ ├── select_account_type_sheet.dart │ ├── select_currency_icu_pattern.dart │ ├── select_currency_sheet.dart │ ├── select_flow_icon_sheet.dart │ ├── select_flow_icon_sheet │ │ ├── select_char_flow_icon_sheet.dart │ │ ├── select_icon_flow_icon_sheet.dart │ │ └── select_image_flow_icon_sheet.dart │ ├── select_multi_currency_sheet.dart │ ├── select_multi_transaction_type_sheet.dart │ ├── select_time_range_mode_sheet.dart │ ├── select_transaction_type_sheet.dart │ ├── year_selector_sheet.dart │ └── year_selector_sheet │ │ └── year_button.dart │ ├── square_map.dart │ ├── theme_petal_selector.dart │ ├── theme_petal_selector │ └── theme_petal_painter.dart │ ├── time_range_selector.dart │ ├── transaction │ └── type_selector.dart │ ├── transaction_filter_head.dart │ ├── transaction_filter_head │ ├── create_filter_preset_sheet.dart │ ├── select_filter_preset_sheet.dart │ ├── select_filter_preset_sheet │ │ ├── default_filter_preset_list_tile.dart │ │ └── filter_preset_list_tile.dart │ ├── select_group_range_sheet.dart │ ├── select_multi_account_sheet.dart │ ├── select_multi_category_sheet.dart │ ├── select_transaction_filter_time_range_sheet.dart │ ├── transaction_filter_chip.dart │ └── transaction_search_sheet.dart │ ├── transaction_list_tile.dart │ ├── transaction_watcher.dart │ ├── transactions_date_header.dart │ ├── trend.dart │ ├── utils │ ├── should_execute_scheduled_task.dart │ ├── time_and_range.dart │ └── utils.dart │ └── year_selector_bar.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── logo@16.png ├── logo@32.png ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ ├── Release.entitlements │ ├── en-IN.lproj │ │ └── MainMenu.strings │ ├── it.lproj │ │ └── MainMenu.strings │ ├── mn.lproj │ │ └── MainMenu.strings │ └── tr.lproj │ │ └── MainMenu.strings └── RunnerTests │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── reformat_l10n_files.sh ├── regenerate_code.sh ├── scripts └── reformat_l10n_files.dart ├── test ├── backup │ ├── v1_export.dart │ ├── v1_import.dart │ ├── v1_populate.dart │ └── v1_test.dart ├── database_test.dart ├── import │ ├── csv_data_parser_test.dart │ ├── invalid-1.csv │ └── valid-1.csv ├── l10n │ └── json_integrity_test.dart ├── objectbox_erase.dart ├── serialization │ └── flow_notification_payload_test.dart └── unit │ └── should_execute_scheduled_task_test.dart └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: flow-mn 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 7 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 8 | liberapay: # Replace with a single Liberapay username 9 | issuehunt: # Replace with a single IssueHunt username 10 | otechie: # Replace with a single Otechie username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | custom: https://buymeacoffee.com/sadespresso 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea or improvement to Flow 3 | title: "[FEAT] " 4 | body: 5 | - type: textarea 6 | id: desc 7 | attributes: 8 | label: Description 9 | description: Describe your suggested feature and the main use cases 10 | validations: 11 | required: true 12 | 13 | - type: textarea 14 | id: context 15 | attributes: 16 | label: Additional Context 17 | description: Add any additonal context about the feature here 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - "develop" 7 | 8 | jobs: 9 | test: 10 | runs-on: "ubuntu-latest" 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: subosito/flutter-action@v2 14 | with: 15 | channel: stable 16 | - run: bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh) 17 | - run: flutter pub get 18 | - run: flutter test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | # ObjectBox unit testing 49 | 50 | .objectbox_test 51 | 52 | # Objectbox library 53 | 54 | /download 55 | lib/libobjectbox.dylib 56 | lib/libobjectbox.so -------------------------------------------------------------------------------- /.idx/dev.nix: -------------------------------------------------------------------------------- 1 | {pkgs}: { 2 | channel = "stable-24.05"; 3 | packages = [ 4 | pkgs.jdk17 5 | pkgs.unzip 6 | ]; 7 | idx.extensions = [ 8 | "Dart-Code.dart-code" 9 | "Dart-Code.flutter" 10 | ]; 11 | idx.previews = { 12 | previews = { 13 | android = { 14 | command = [ 15 | "flutter" 16 | "run" 17 | "--machine" 18 | "-d" 19 | "android" 20 | "-d" 21 | "localhost:5555" 22 | ]; 23 | manager = "flutter"; 24 | }; 25 | }; 26 | }; 27 | } -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": { 3 | "line_length": 120 4 | }, 5 | "MD024": false, 6 | "MD041": false 7 | } 8 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 17 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 18 | - platform: web 19 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 20 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": false 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Dart-Code.dart-code", 4 | "Dart-Code.flutter", 5 | "davidanson.vscode-markdownlint" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flow", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.configureOnOpen": false, 3 | "IDX.aI.enableInlineCompletion": true, 4 | "IDX.aI.enableCodebaseIndexing": true, 5 | "editor.tabSize": 2 6 | } 7 | -------------------------------------------------------------------------------- /RELEASE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## **PLEASE BACKUP BEFORE UPGRADING** 2 | 3 | You may permanently lose all of your data if something goes wrong with the update. 4 | Generally, everything should be fine unless specified otherwise in the release note. 5 | Project owner and/or any of the project contributors are not responsible for your 6 | data loss under any circumstances! 7 | 8 | ## What's Changed 9 | 10 | {{changelog}} 11 | 12 | ## Download 13 | 14 | * [Google Play](https://play.google.com/store/apps/details?id=mn.flow.flow?utm_source=gh-release-{{version}}) for Android 15 | * [App Store](https://apps.apple.com/mn/app/flow-expense-tracker/id6477741670?utm_source=gh-release-{{version}}) for iOS 16 | 17 | Also: 18 | 19 | * Download Fat APK build from [GitHub release](https://github.com/flow-mn/flow/releases/latest) 20 | 21 | Updates may take some time to appear on app stores after GitHub release 22 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | unawaited_futures: true 6 | prefer_double_quotes: true 7 | type_annotate_public_apis: true 8 | analyzer: 9 | exclude: 10 | - "example/**" 11 | - "build/**" 12 | - "**/*.g.dart" 13 | - "**/*.freezed.dart" 14 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | build -------------------------------------------------------------------------------- /android/app/.gitignore: -------------------------------------------------------------------------------- 1 | .cxx -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/mn/flow/flow/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package mn.flow.flow 2 | 3 | import io.flutter.embedding.android.FlutterFragmentActivity 4 | 5 | class MainActivity: FlutterFragmentActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-hdpi/monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/notification_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-hdpi/notification_default.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-mdpi/monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/notification_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-mdpi/notification_default.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xhdpi/monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/notification_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xhdpi/notification_default.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxhdpi/monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/notification_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxhdpi/notification_default.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxxhdpi/monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/notification_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable-xxxhdpi/notification_default.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/notification_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/drawable/notification_default.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2560M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip 6 | distributionSha256Sum=7ebdac923867a3cec0098302416d1e3c6c0c729fc4e2e05c10637a8af33a76c5 7 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # For https://pub.dev/packages/file_picker 2 | -keep class androidx.lifecycle.DefaultLifecycleObserver 3 | 4 | ## Gson rules 5 | # Gson uses generic type information stored in a class file when working with fields. Proguard 6 | # removes such information by default, so configure it to keep all of it. 7 | -keepattributes Signature 8 | 9 | # For using GSON @Expose annotation 10 | -keepattributes *Annotation* 11 | 12 | # Gson specific classes 13 | -dontwarn sun.misc.** 14 | #-keep class com.google.gson.stream.** { *; } 15 | 16 | # Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, 17 | # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) 18 | -keep class * extends com.google.gson.TypeAdapter 19 | -keep class * implements com.google.gson.TypeAdapterFactory 20 | -keep class * implements com.google.gson.JsonSerializer 21 | -keep class * implements com.google.gson.JsonDeserializer 22 | 23 | # Prevent R8 from leaving Data object members always null 24 | -keepclassmembers,allowobfuscation class * { 25 | @com.google.gson.annotations.SerializedName ; 26 | } 27 | 28 | # Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. 29 | -keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken 30 | -keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | } 19 | 20 | plugins { 21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 22 | id "com.android.application" version "8.8.0" apply false 23 | id "org.jetbrains.kotlin.android" version "1.9.10" apply false 24 | } 25 | 26 | include ":app" 27 | -------------------------------------------------------------------------------- /assets/fonts/NotoEmoji-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/NotoEmoji-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/NotoSansArabic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/NotoSansArabic-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/NotoSansHebrew-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/NotoSansHebrew-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/Poppins-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/Poppins-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/Poppins-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/Poppins-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/fonts/Poppins-SemiBold.ttf -------------------------------------------------------------------------------- /assets/images/2.0x/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/2.0x/flow.png -------------------------------------------------------------------------------- /assets/images/2.0x/map_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/2.0x/map_square.png -------------------------------------------------------------------------------- /assets/images/2.0x/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/2.0x/pin.png -------------------------------------------------------------------------------- /assets/images/3.0x/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/3.0x/flow.png -------------------------------------------------------------------------------- /assets/images/3.0x/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/3.0x/pin.png -------------------------------------------------------------------------------- /assets/images/4.0x/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/4.0x/flow.png -------------------------------------------------------------------------------- /assets/images/external/ivy_wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/external/ivy_wallet.png -------------------------------------------------------------------------------- /assets/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/flow.png -------------------------------------------------------------------------------- /assets/images/map_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/map_square.png -------------------------------------------------------------------------------- /assets/images/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/missing.png -------------------------------------------------------------------------------- /assets/images/missing.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/images/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/assets/images/pin.png -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/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 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | if #available(iOS 10.0, *) { 11 | UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate 12 | } 13 | 14 | GeneratedPluginRegistrant.register(with: self) 15 | 16 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/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/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/blissfulBerry-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/blissfulBerry-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/blissfulBerry-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/blissfulBerry-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/blissfulBerry@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/blissfulBerry@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/blissfulBerry@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/blissfulBerry@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/bohemianBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/bohemianBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/bohemianBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/bohemianBlue-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/bohemianBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/bohemianBlue@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/bohemianBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/bohemianBlue@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/burntSienna-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/burntSienna-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/burntSienna-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/burntSienna-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/burntSienna@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/burntSienna@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/burntSienna@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/burntSienna@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/cherryPlum-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/cherryPlum-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/cherryPlum-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/cherryPlum-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/cherryPlum@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/cherryPlum@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/cherryPlum@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/cherryPlum@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/crispChristmasCranberries-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/crispChristmasCranberries-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/crispChristmasCranberries-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/crispChristmasCranberries-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/crispChristmasCranberries@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/crispChristmasCranberries@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/crispChristmasCranberries@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/crispChristmasCranberries@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/egyptianBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/egyptianBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/egyptianBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/egyptianBlue-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/egyptianBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/egyptianBlue@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/egyptianBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/egyptianBlue@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/flagGreen-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/flagGreen-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/flagGreen-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/flagGreen-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/flagGreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/flagGreen@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/flagGreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/flagGreen@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/hydraTurquoise-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/hydraTurquoise-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/hydraTurquoise-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/hydraTurquoise-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/hydraTurquoise@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/hydraTurquoise@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/hydraTurquoise@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/hydraTurquoise@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/peacockBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/peacockBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/peacockBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/peacockBlue-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/peacockBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/peacockBlue@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/peacockBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/peacockBlue@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/shadeOfViolet-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/shadeOfViolet-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/shadeOfViolet-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/shadeOfViolet-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/shadeOfViolet@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/shadeOfViolet@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/shadeOfViolet@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/shadeOfViolet@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/soilOfAvagddu-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/soilOfAvagddu-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/soilOfAvagddu-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/soilOfAvagddu-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/soilOfAvagddu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/soilOfAvagddu@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/soilOfAvagddu@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/soilOfAvagddu@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spaceBattleBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spaceBattleBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spaceBattleBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spaceBattleBlue-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spaceBattleBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spaceBattleBlue@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spaceBattleBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spaceBattleBlue@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spreadsheetGreen-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spreadsheetGreen-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spreadsheetGreen-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spreadsheetGreen-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spreadsheetGreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spreadsheetGreen@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/spreadsheetGreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/spreadsheetGreen@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tokiwaGreen-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tokiwaGreen-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tokiwaGreen-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tokiwaGreen-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tokiwaGreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tokiwaGreen@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tokiwaGreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tokiwaGreen@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/toyCamouflage-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/toyCamouflage-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/toyCamouflage-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/toyCamouflage-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/toyCamouflage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/toyCamouflage@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/toyCamouflage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/toyCamouflage@3x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tropicana-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tropicana-ipad-pro@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tropicana-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tropicana-ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tropicana@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tropicana@2x.png -------------------------------------------------------------------------------- /ios/Runner/LauncherIcons/tropicana@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/ios/Runner/LauncherIcons/tropicana@3x.png -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.icloud-container-identifiers 8 | 9 | iCloud.mn.flow.flow 10 | 11 | com.apple.developer.icloud-services 12 | 13 | CloudKit 14 | CloudDocuments 15 | 16 | com.apple.developer.ubiquity-container-identifiers 17 | 18 | iCloud.mn.flow.flow 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /launcher_icons/adaptive_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/adaptive_background.png -------------------------------------------------------------------------------- /launcher_icons/adaptive_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/adaptive_foreground.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/blissfulBerry-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/blissfulBerry-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/blissfulBerry-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/blissfulBerry-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/blissfulBerry@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/blissfulBerry@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/blissfulBerry@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/blissfulBerry@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/bohemianBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/bohemianBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/bohemianBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/bohemianBlue-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/bohemianBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/bohemianBlue@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/bohemianBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/bohemianBlue@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/burntSienna-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/burntSienna-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/burntSienna-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/burntSienna-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/burntSienna@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/burntSienna@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/burntSienna@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/burntSienna@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/cherryPlum-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/cherryPlum-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/cherryPlum-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/cherryPlum-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/cherryPlum@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/cherryPlum@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/cherryPlum@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/cherryPlum@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/crispChristmasCranberries-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/crispChristmasCranberries-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/crispChristmasCranberries-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/crispChristmasCranberries-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/crispChristmasCranberries@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/crispChristmasCranberries@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/crispChristmasCranberries@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/crispChristmasCranberries@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/egyptianBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/egyptianBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/egyptianBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/egyptianBlue-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/egyptianBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/egyptianBlue@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/egyptianBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/egyptianBlue@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/flagGreen-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/flagGreen-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/flagGreen-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/flagGreen-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/flagGreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/flagGreen@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/flagGreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/flagGreen@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/hydraTurquoise-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/hydraTurquoise-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/hydraTurquoise-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/hydraTurquoise-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/hydraTurquoise@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/hydraTurquoise@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/hydraTurquoise@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/hydraTurquoise@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/peacockBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/peacockBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/peacockBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/peacockBlue-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/peacockBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/peacockBlue@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/peacockBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/peacockBlue@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/shadeOfViolet-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/shadeOfViolet-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/shadeOfViolet-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/shadeOfViolet-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/shadeOfViolet@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/shadeOfViolet@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/shadeOfViolet@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/shadeOfViolet@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/soilOfAvagddu-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/soilOfAvagddu-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/soilOfAvagddu-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/soilOfAvagddu-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/soilOfAvagddu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/soilOfAvagddu@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/soilOfAvagddu@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/soilOfAvagddu@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spaceBattleBlue-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spaceBattleBlue-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spaceBattleBlue-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spaceBattleBlue-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spaceBattleBlue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spaceBattleBlue@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spaceBattleBlue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spaceBattleBlue@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spreadsheetGreen-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spreadsheetGreen-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spreadsheetGreen-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spreadsheetGreen-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spreadsheetGreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spreadsheetGreen@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/spreadsheetGreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/spreadsheetGreen@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tokiwaGreen-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tokiwaGreen-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tokiwaGreen-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tokiwaGreen-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tokiwaGreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tokiwaGreen@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tokiwaGreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tokiwaGreen@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/toyCamouflage-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/toyCamouflage-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/toyCamouflage-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/toyCamouflage-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/toyCamouflage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/toyCamouflage@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/toyCamouflage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/toyCamouflage@3x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tropicana-ipad-pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tropicana-ipad-pro@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tropicana-ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tropicana-ipad@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tropicana@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tropicana@2x.png -------------------------------------------------------------------------------- /launcher_icons/defaults-light/tropicana@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/defaults-light/tropicana@3x.png -------------------------------------------------------------------------------- /launcher_icons/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/flow.png -------------------------------------------------------------------------------- /launcher_icons/monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/launcher_icons/monochrome.png -------------------------------------------------------------------------------- /lib/constants.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/foundation.dart"; 2 | import "package:latlong2/latlong.dart"; 3 | 4 | String? downloadedFrom; 5 | 6 | String appVersion = "0.0.0"; 7 | const bool debugBuild = false; 8 | 9 | bool get flowDebugMode => kDebugMode || debugBuild; 10 | 11 | final Uri discordInviteLink = Uri.parse("https://discord.gg/Ndh9VDeZa4"); 12 | final Uri maintainerKoFiLink = Uri.parse("https://flow.gege.mn/donate"); 13 | final Uri website = Uri.parse("https://flow.gege.mn"); 14 | final Uri flowGitHubRepoLink = Uri.parse("https://github.com/flow-mn/flow"); 15 | final Uri flowGitHubIssuesLink = Uri.parse( 16 | "https://github.com/flow-mn/flow/issues", 17 | ); 18 | final Uri maintainerGitHubLink = Uri.parse("https://github.com/sadespresso"); 19 | 20 | const double sukhbaatarSquareCenterLat = 47.918828; 21 | const double sukhbaatarSquareCenterLong = 106.917604; 22 | 23 | const String appleAppStoreId = "6477741670"; 24 | 25 | final Uri csvImportTemplateUrl = Uri.parse( 26 | "https://docs.google.com/spreadsheets/d/1wxdJ1T8PSvzayxvGs7bVyqQ9Zu0DPQ1YwiBLy1FluqE/edit?usp=sharing", 27 | ); 28 | 29 | const LatLng sukhbaatarSquareCenter = LatLng( 30 | sukhbaatarSquareCenterLat, 31 | sukhbaatarSquareCenterLong, 32 | ); 33 | -------------------------------------------------------------------------------- /lib/data/chart_data.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/money.dart"; 2 | import "package:flow/services/exchange_rates.dart"; 3 | 4 | class ChartData implements Comparable> { 5 | final String key; 6 | final Money money; 7 | final String currency; 8 | final T? associatedData; 9 | 10 | double get displayTotal => money.amount.abs(); 11 | 12 | ChartData({ 13 | required this.key, 14 | required this.money, 15 | required this.currency, 16 | required this.associatedData, 17 | }); 18 | 19 | @override 20 | int compareTo(ChartData other) { 21 | return money.tryCompareToWithExchange( 22 | other.money, 23 | ExchangeRatesService().getPrimaryCurrencyRates(), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/data/exchange_rates.dart: -------------------------------------------------------------------------------- 1 | import "package:moment_dart/moment_dart.dart"; 2 | 3 | /// Uses endpoints from here: 4 | class ExchangeRates { 5 | final DateTime date; 6 | final String baseCurrency; 7 | final Map rates; 8 | 9 | const ExchangeRates({ 10 | required this.date, 11 | required this.baseCurrency, 12 | required this.rates, 13 | }); 14 | 15 | factory ExchangeRates.fromJson(Map json) { 16 | final String baseCurrency = json.keys.firstWhere((key) => key != "date"); 17 | 18 | return ExchangeRates( 19 | date: DateTime.parse(json["date"]), 20 | baseCurrency: baseCurrency, 21 | rates: Map.from(json[baseCurrency.toLowerCase()]), 22 | ); 23 | } 24 | 25 | Map toJson() { 26 | return {"date": date.format(payload: "YYYY-MM-DD"), baseCurrency: rates}; 27 | } 28 | 29 | double? getRate(String currency) { 30 | return rates[currency.toLowerCase()]?.toDouble(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/data/exchange_rates_set.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/exchange_rates.dart"; 2 | 3 | class ExchangeRatesSet { 4 | final Map rates; 5 | 6 | ExchangeRatesSet(this.rates); 7 | 8 | void set(String baseCurrency, ExchangeRates exchangeRates) { 9 | rates[baseCurrency] = exchangeRates; 10 | } 11 | 12 | ExchangeRates? get(String baseCurrency) { 13 | return rates[baseCurrency]; 14 | } 15 | 16 | factory ExchangeRatesSet.fromJson(Map json) { 17 | final Map rates = {}; 18 | 19 | for (final String baseCurrency in json.keys) { 20 | rates[baseCurrency] = ExchangeRates.fromJson(json[baseCurrency]); 21 | } 22 | 23 | return ExchangeRatesSet(rates); 24 | } 25 | 26 | Map toJson() { 27 | final Map json = {}; 28 | 29 | for (final MapEntry entry in rates.entries) { 30 | json[entry.key] = entry.value.toJson(); 31 | } 32 | 33 | return json; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/data/flow_analytics.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/multi_currency_flow.dart"; 2 | import "package:moment_dart/moment_dart.dart"; 3 | 4 | class FlowAnalytics { 5 | final TimeRange range; 6 | 7 | final Map> flow; 8 | 9 | const FlowAnalytics({required this.range, required this.flow}); 10 | } 11 | -------------------------------------------------------------------------------- /lib/data/flow_notification_payload.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'flow_notification_payload.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | FlowNotificationPayload _$FlowNotificationPayloadFromJson( 10 | Map json, 11 | ) => FlowNotificationPayload( 12 | itemType: $enumDecode( 13 | _$FlowNotificationPayloadItemTypeEnumMap, 14 | json['itemType'], 15 | ), 16 | id: json['id'] as String?, 17 | extra: json['extra'] as String?, 18 | ); 19 | 20 | Map _$FlowNotificationPayloadToJson( 21 | FlowNotificationPayload instance, 22 | ) => { 23 | 'itemType': _$FlowNotificationPayloadItemTypeEnumMap[instance.itemType]!, 24 | 'id': instance.id, 25 | 'extra': instance.extra, 26 | }; 27 | 28 | const _$FlowNotificationPayloadItemTypeEnumMap = { 29 | FlowNotificationPayloadItemType.transaction: 'txn', 30 | FlowNotificationPayloadItemType.reminder: 'rmd', 31 | }; 32 | -------------------------------------------------------------------------------- /lib/data/github/contributor.dart: -------------------------------------------------------------------------------- 1 | /// Data class for GitHub contributor 2 | class GitHubContributor { 3 | final int id; 4 | final String login; 5 | final String avatarUrl; 6 | final int contributions; 7 | final String htmlUrl; 8 | final String nodeId; 9 | 10 | factory GitHubContributor.fromJson(Map json) { 11 | return GitHubContributor( 12 | id: json["id"] as int, 13 | login: json["login"] as String, 14 | avatarUrl: json["avatar_url"] as String, 15 | contributions: json["contributions"] as int, 16 | htmlUrl: json["html_url"] as String, 17 | nodeId: json["node_id"] as String, 18 | ); 19 | } 20 | const GitHubContributor({ 21 | required this.login, 22 | required this.avatarUrl, 23 | required this.contributions, 24 | required this.htmlUrl, 25 | required this.nodeId, 26 | required this.id, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /lib/data/prefs/frecency.dart: -------------------------------------------------------------------------------- 1 | import "package:json_annotation/json_annotation.dart"; 2 | 3 | part "frecency.g.dart"; 4 | 5 | @JsonSerializable() 6 | class FrecencyData { 7 | final String uuid; 8 | 9 | final DateTime lastUsed; 10 | 11 | final int useCount; 12 | 13 | const FrecencyData({ 14 | required this.uuid, 15 | required this.lastUsed, 16 | required this.useCount, 17 | }); 18 | 19 | FrecencyData incremented([int increment = 1]) { 20 | return FrecencyData( 21 | useCount: useCount + increment, 22 | lastUsed: DateTime.now(), 23 | uuid: uuid, 24 | ); 25 | } 26 | 27 | @JsonKey(includeFromJson: false, includeToJson: false) 28 | double get score { 29 | final Duration sinceLastUsed = DateTime.now().difference(lastUsed); 30 | 31 | return switch (sinceLastUsed) { 32 | >= const Duration(days: 60) => useCount * 0.2, 33 | >= const Duration(days: 30) => useCount * 0.5, 34 | >= const Duration(days: 14) => useCount * 0.67, 35 | >= const Duration(days: 7) => useCount * 0.875, 36 | >= const Duration(hours: 72) => useCount * 1.0, 37 | >= const Duration(hours: 24) => useCount * 2.0, 38 | >= const Duration(hours: 8) => useCount * 3.0, 39 | _ => useCount * 4.0, 40 | }; 41 | } 42 | 43 | factory FrecencyData.fromJson(Map json) => 44 | _$FrecencyDataFromJson(json); 45 | Map toJson() => _$FrecencyDataToJson(this); 46 | } 47 | -------------------------------------------------------------------------------- /lib/data/prefs/frecency.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'frecency.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | FrecencyData _$FrecencyDataFromJson(Map json) => FrecencyData( 10 | uuid: json['uuid'] as String, 11 | lastUsed: DateTime.parse(json['lastUsed'] as String), 12 | useCount: (json['useCount'] as num).toInt(), 13 | ); 14 | 15 | Map _$FrecencyDataToJson(FrecencyData instance) => 16 | { 17 | 'uuid': instance.uuid, 18 | 'lastUsed': instance.lastUsed.toIso8601String(), 19 | 'useCount': instance.useCount, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/data/prefs/frecency_group.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/prefs/frecency.dart"; 2 | import "package:flow/utils/utils.dart"; 3 | import "package:json_annotation/json_annotation.dart"; 4 | 5 | part "frecency_group.g.dart"; 6 | 7 | @JsonSerializable() 8 | class FrecencyGroup { 9 | final List data; 10 | 11 | const FrecencyGroup(this.data); 12 | 13 | double getScore(String uuid) => 14 | data.firstWhereOrNull((element) => element.uuid == uuid)?.score ?? 0.0; 15 | 16 | factory FrecencyGroup.fromJson(Map json) => 17 | _$FrecencyGroupFromJson(json); 18 | Map toJson() => _$FrecencyGroupToJson(this); 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/prefs/frecency_group.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'frecency_group.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | FrecencyGroup _$FrecencyGroupFromJson(Map json) => 10 | FrecencyGroup( 11 | (json['data'] as List) 12 | .map((e) => FrecencyData.fromJson(e as Map)) 13 | .toList(), 14 | ); 15 | 16 | Map _$FrecencyGroupToJson(FrecencyGroup instance) => 17 | {'data': instance.data}; 18 | -------------------------------------------------------------------------------- /lib/data/recurrence_mode.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/named_enum.dart"; 2 | import "package:json_annotation/json_annotation.dart"; 3 | 4 | @JsonEnum(valueField: "value") 5 | enum RecurrenceMode implements LocalizedEnum { 6 | everyDay("everyDay"), 7 | everyWeek("everyWeek"), 8 | every2Week("every2Week"), 9 | everyMonth("everyMonth"), 10 | everyYear("everyYear"), 11 | custom("custom"); 12 | 13 | final String value; 14 | 15 | const RecurrenceMode(this.value); 16 | 17 | @override 18 | String get localizationEnumName => "RecurrenceMode"; 19 | 20 | @override 21 | String get localizationEnumValue => value; 22 | } 23 | -------------------------------------------------------------------------------- /lib/data/setup/default_accounts.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/entity/account.dart"; 3 | import "package:flow/l10n/extensions.dart"; 4 | import "package:material_symbols_icons/symbols.dart"; 5 | 6 | List getAccountPresets(String currency) { 7 | return [ 8 | Account.preset( 9 | name: "setup.accounts.preset.main".tr(), 10 | currency: currency, 11 | iconCode: FlowIconData.icon(Symbols.credit_card_rounded).toString(), 12 | uuid: "864df1dc-fe59-47e0-8423-98d8f86453b6", 13 | ), 14 | Account.preset( 15 | name: "setup.accounts.preset.cash".tr(), 16 | currency: currency, 17 | iconCode: FlowIconData.icon(Symbols.payments_rounded).toString(), 18 | uuid: "d7ef9672-256b-4097-a55a-27a58c6f5ba5", 19 | ), 20 | Account.preset( 21 | name: "setup.accounts.preset.savings".tr(), 22 | currency: currency, 23 | iconCode: FlowIconData.icon(Symbols.savings_rounded).toString(), 24 | excludeFromTotalBalance: true, 25 | uuid: "c04e1cdd-842f-48c1-9c6c-d07fb2b09193", 26 | ), 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /lib/data/transactions_filter/group_range.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/transaction.dart"; 2 | import "package:flow/l10n/named_enum.dart"; 3 | import "package:json_annotation/json_annotation.dart"; 4 | import "package:moment_dart/moment_dart.dart"; 5 | 6 | @JsonEnum(valueField: "value") 7 | enum TransactionGroupRange implements LocalizedEnum { 8 | hour("hour"), 9 | 10 | /// Default 11 | day("day"), 12 | week("week"), 13 | month("month"), 14 | year("year"), 15 | allTime("allTime"); 16 | 17 | final String value; 18 | 19 | const TransactionGroupRange(this.value); 20 | 21 | @override 22 | String get localizationEnumName => "TransactionGroupRange"; 23 | 24 | @override 25 | String get localizationEnumValue => name; 26 | 27 | TimeRange fromTransaction(Transaction t) => switch (this) { 28 | TransactionGroupRange.hour => HourTimeRange.fromDateTime(t.transactionDate), 29 | TransactionGroupRange.day => DayTimeRange.fromDateTime(t.transactionDate), 30 | TransactionGroupRange.week => LocalWeekTimeRange(t.transactionDate), 31 | TransactionGroupRange.month => MonthTimeRange.fromDateTime( 32 | t.transactionDate, 33 | ), 34 | TransactionGroupRange.year => YearTimeRange.fromDateTime(t.transactionDate), 35 | TransactionGroupRange.allTime => TimeRange.allTime(), 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /lib/data/transactions_filter/search_data.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'search_data.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TransactionSearchData _$TransactionSearchDataFromJson( 10 | Map json, 11 | ) => TransactionSearchData( 12 | keyword: json['keyword'] as String?, 13 | mode: 14 | $enumDecodeNullable(_$TransactionSearchModeEnumMap, json['mode']) ?? 15 | TransactionSearchMode.smart, 16 | smartMatchThreshold: 17 | (json['smartMatchThreshold'] as num?)?.toDouble() ?? 80.0, 18 | includeDescription: json['includeDescription'] as bool? ?? true, 19 | ); 20 | 21 | Map _$TransactionSearchDataToJson( 22 | TransactionSearchData instance, 23 | ) => { 24 | 'keyword': instance.keyword, 25 | 'mode': _$TransactionSearchModeEnumMap[instance.mode]!, 26 | 'includeDescription': instance.includeDescription, 27 | 'smartMatchThreshold': instance.smartMatchThreshold, 28 | }; 29 | 30 | const _$TransactionSearchModeEnumMap = { 31 | TransactionSearchMode.smart: 'smart', 32 | TransactionSearchMode.substring: 'substring', 33 | TransactionSearchMode.exact: 'exact', 34 | TransactionSearchMode.none: 'none', 35 | }; 36 | -------------------------------------------------------------------------------- /lib/data/transactions_filter/sort_field.dart: -------------------------------------------------------------------------------- 1 | import "package:json_annotation/json_annotation.dart"; 2 | 3 | @JsonEnum(valueField: "value") 4 | enum TransactionSortField { 5 | /// Default 6 | transactionDate("transactionDate"), 7 | amount("amount"), 8 | createdDate("createdDate"); 9 | 10 | final String value; 11 | 12 | const TransactionSortField(this.value); 13 | } 14 | -------------------------------------------------------------------------------- /lib/entity/_base.dart: -------------------------------------------------------------------------------- 1 | import "package:objectbox/objectbox.dart"; 2 | 3 | abstract class EntityBase { 4 | String get uuid; 5 | } 6 | 7 | extension ToOneRelationSerializer on ToOne { 8 | String? relationToJson() => target?.uuid; 9 | String relationToJsonForced() => target!.uuid; 10 | } 11 | -------------------------------------------------------------------------------- /lib/entity/category.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'category.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Category _$CategoryFromJson(Map json) => Category( 10 | name: json['name'] as String, 11 | iconCode: json['iconCode'] as String, 12 | createdDate: _$JsonConverterFromJson( 13 | json['createdDate'], 14 | const UTCDateTimeConverter().fromJson, 15 | ), 16 | )..uuid = json['uuid'] as String; 17 | 18 | Map _$CategoryToJson(Category instance) => { 19 | 'uuid': instance.uuid, 20 | 'createdDate': const UTCDateTimeConverter().toJson(instance.createdDate), 21 | 'name': instance.name, 22 | 'iconCode': instance.iconCode, 23 | }; 24 | 25 | Value? _$JsonConverterFromJson( 26 | Object? json, 27 | Value? Function(Json json) fromJson, 28 | ) => json == null ? null : fromJson(json as Json); 29 | -------------------------------------------------------------------------------- /lib/entity/profile.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/_base.dart"; 2 | import "package:flow/objectbox.dart"; 3 | import "package:flow/utils/json/utc_datetime_converter.dart"; 4 | import "package:json_annotation/json_annotation.dart"; 5 | import "package:objectbox/objectbox.dart"; 6 | import "package:uuid/uuid.dart"; 7 | 8 | part "profile.g.dart"; 9 | 10 | @Entity() 11 | @JsonSerializable(explicitToJson: true, converters: [UTCDateTimeConverter()]) 12 | class Profile implements EntityBase { 13 | @JsonKey(includeFromJson: false, includeToJson: false) 14 | int id; 15 | 16 | @override 17 | @Unique() 18 | String uuid; 19 | 20 | static const int maxNameLength = 96; 21 | 22 | String name; 23 | 24 | @Property(type: PropertyType.date) 25 | DateTime createdDate; 26 | 27 | @Transient() 28 | String get imagePath => "$uuid.png"; 29 | 30 | Profile({this.id = 0, DateTime? createdDate, required this.name}) 31 | : createdDate = createdDate ?? DateTime.now(), 32 | uuid = const Uuid().v4(); 33 | 34 | factory Profile.fromJson(Map json) => 35 | _$ProfileFromJson(json); 36 | Map toJson() => _$ProfileToJson(this); 37 | 38 | static void createDefaultProfile() { 39 | ObjectBox().box().put(Profile(name: "Default Profile")); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/entity/profile.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'profile.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Profile _$ProfileFromJson(Map json) => Profile( 10 | createdDate: _$JsonConverterFromJson( 11 | json['createdDate'], 12 | const UTCDateTimeConverter().fromJson, 13 | ), 14 | name: json['name'] as String, 15 | )..uuid = json['uuid'] as String; 16 | 17 | Map _$ProfileToJson(Profile instance) => { 18 | 'uuid': instance.uuid, 19 | 'name': instance.name, 20 | 'createdDate': const UTCDateTimeConverter().toJson(instance.createdDate), 21 | }; 22 | 23 | Value? _$JsonConverterFromJson( 24 | Object? json, 25 | Value? Function(Json json) fromJson, 26 | ) => json == null ? null : fromJson(json as Json); 27 | -------------------------------------------------------------------------------- /lib/entity/transaction/extensions/base.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/utils/jasonable.dart"; 2 | 3 | abstract class TransactionExtension implements Jasonable { 4 | final String uuid; 5 | 6 | String get extensionExistenceTag => "hasExtension:$key"; 7 | String get extensionIdentifierTag => "$key:$uuid"; 8 | 9 | String get key; 10 | String? get relatedTransactionUuid; 11 | set relatedTransactionUuid(String? uuid); 12 | 13 | void setRelatedTransactionUuid(String uuid) => 14 | relatedTransactionUuid = relatedTransactionUuid; 15 | 16 | const TransactionExtension({required this.uuid}); 17 | } 18 | -------------------------------------------------------------------------------- /lib/entity/transaction/extensions/default/recurring.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'recurring.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Recurring _$RecurringFromJson(Map json) => Recurring( 10 | uuid: json['uuid'] as String, 11 | initialTransactionDate: DateTime.parse( 12 | json['initialTransactionDate'] as String, 13 | ), 14 | relatedTransactionUuid: json['relatedTransactionUuid'] as String?, 15 | locked: json['locked'] as bool? ?? false, 16 | ); 17 | 18 | Map _$RecurringToJson(Recurring instance) => { 19 | 'uuid': instance.uuid, 20 | 'relatedTransactionUuid': instance.relatedTransactionUuid, 21 | 'initialTransactionDate': instance.initialTransactionDate.toIso8601String(), 22 | 'locked': instance.locked, 23 | 'key': instance.key, 24 | }; 25 | -------------------------------------------------------------------------------- /lib/entity/transaction/extensions/default/transfer.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'transfer.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Transfer _$TransferFromJson(Map json) => Transfer( 10 | uuid: json['uuid'] as String, 11 | fromAccountUuid: json['fromAccountUuid'] as String, 12 | toAccountUuid: json['toAccountUuid'] as String, 13 | relatedTransactionUuid: json['relatedTransactionUuid'] as String?, 14 | conversionRate: (json['conversionRate'] as num?)?.toDouble(), 15 | ); 16 | 17 | Map _$TransferToJson(Transfer instance) => { 18 | 'uuid': instance.uuid, 19 | 'key': instance.key, 20 | 'fromAccountUuid': instance.fromAccountUuid, 21 | 'toAccountUuid': instance.toAccountUuid, 22 | 'conversionRate': instance.conversionRate, 23 | 'relatedTransactionUuid': instance.relatedTransactionUuid, 24 | }; 25 | -------------------------------------------------------------------------------- /lib/entity/transaction/subtype.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/named_enum.dart"; 2 | import "package:json_annotation/json_annotation.dart"; 3 | 4 | @JsonEnum(valueField: "value") 5 | enum TransactionSubtype implements LocalizedEnum { 6 | transactionFee("transactionFee"), 7 | givenLoan("loan.given"), 8 | receivedLoan("loan.received"), 9 | updateBalance("updateBalance"); 10 | 11 | final String value; 12 | 13 | const TransactionSubtype(this.value); 14 | 15 | @override 16 | String get localizationEnumValue => name; 17 | @override 18 | String get localizationEnumName => "TransactionSubtype"; 19 | } 20 | -------------------------------------------------------------------------------- /lib/entity/transaction/type.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/named_enum.dart"; 2 | import "package:json_annotation/json_annotation.dart"; 3 | 4 | @JsonEnum(valueField: "value") 5 | enum TransactionType implements LocalizedEnum { 6 | transfer("transfer"), 7 | income("income"), 8 | expense("expense"); 9 | 10 | final String value; 11 | 12 | const TransactionType(this.value); 13 | 14 | @override 15 | String get localizationEnumValue => name; 16 | @override 17 | String get localizationEnumName => "TransactionType"; 18 | } 19 | -------------------------------------------------------------------------------- /lib/entity/transaction_filter_preset.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'transaction_filter_preset.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TransactionFilterPreset _$TransactionFilterPresetFromJson( 10 | Map json, 11 | ) => TransactionFilterPreset( 12 | createdDate: _$JsonConverterFromJson( 13 | json['createdDate'], 14 | const UTCDateTimeConverter().fromJson, 15 | ), 16 | jsonTransactionFilter: json['jsonTransactionFilter'] as String, 17 | name: json['name'] as String, 18 | )..uuid = json['uuid'] as String; 19 | 20 | Map _$TransactionFilterPresetToJson( 21 | TransactionFilterPreset instance, 22 | ) => { 23 | 'uuid': instance.uuid, 24 | 'name': instance.name, 25 | 'jsonTransactionFilter': instance.jsonTransactionFilter, 26 | 'createdDate': const UTCDateTimeConverter().toJson(instance.createdDate), 27 | }; 28 | 29 | Value? _$JsonConverterFromJson( 30 | Object? json, 31 | Value? Function(Json json) fromJson, 32 | ) => json == null ? null : fromJson(json as Json); 33 | -------------------------------------------------------------------------------- /lib/form_validators.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | 3 | String? validateRequiredField(String? input) { 4 | if (input == null || input.isEmpty || input.trim().isEmpty) { 5 | return "error.input.mustBeNotEmpty".tr(); 6 | } 7 | 8 | return null; 9 | } 10 | -------------------------------------------------------------------------------- /lib/l10n/extensions.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/flow_localizations.dart"; 2 | import "package:flow/l10n/supported_languages.dart"; 3 | import "package:flutter/widgets.dart"; 4 | 5 | extension L10nHelper on BuildContext { 6 | FlowLocalizations get l => FlowLocalizations.of(this); 7 | } 8 | 9 | extension Underscore on Locale { 10 | /// Example outcome: 11 | /// * en 12 | /// * fr_FR 13 | /// * mn_Mong_MN 14 | String get code => [languageCode, scriptCode, countryCode].nonNulls.join("_"); 15 | 16 | /// English name 17 | String get name => supportedLanguages[this]?.$1 ?? "Unknown"; 18 | 19 | /// Language name in the language 20 | String get endonym => supportedLanguages[this]?.$2 ?? "Unknown"; 21 | } 22 | 23 | extension L10nStringHelper on String { 24 | /// Returns localized version of [this]. 25 | /// 26 | /// Same as calling context.l.get([this]) 27 | String t(BuildContext context, [dynamic replace]) => 28 | context.l.get(this, replace: replace); 29 | 30 | /// Returns localized version of [this]. 31 | /// 32 | /// This does not require a context 33 | String tr([dynamic replace]) => 34 | FlowLocalizations.getTransalation(this, replace: replace); 35 | } 36 | -------------------------------------------------------------------------------- /lib/l10n/localized_exception.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | abstract class LocalizedException { 5 | final String l10nKey; 6 | final dynamic l10nArgs; 7 | 8 | String localizedString(BuildContext context) => l10nKey.t(context, l10nArgs); 9 | 10 | const LocalizedException({required this.l10nKey, this.l10nArgs}); 11 | } 12 | -------------------------------------------------------------------------------- /lib/l10n/named_enum.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | abstract class LocalizedEnum { 5 | String get localizationEnumValue; 6 | String get localizationEnumName; 7 | } 8 | 9 | extension LocalizedNameEnums on LocalizedEnum { 10 | String get localizedTextKey => 11 | "enum.$localizationEnumName@$localizationEnumValue"; 12 | 13 | String get localizedName => localizedTextKey.tr(); 14 | String localizedNameContext(BuildContext context, [dynamic replace]) => 15 | localizedTextKey.t(context, replace); 16 | } 17 | -------------------------------------------------------------------------------- /lib/l10n/supported_languages.dart: -------------------------------------------------------------------------------- 1 | import "dart:ui"; 2 | 3 | /// Locale - (English name, Endonym) 4 | /// 5 | /// * You have to include a country if the [Locale.countryCode] is not null. 6 | /// * Your file name must match `languageCode_scriptCode_countryCode` format. But both `scriptCode` and `countryCode` is optional. 7 | final Map supportedLanguages = { 8 | const Locale("mn", "MN"): ("Mongolian (Mongolia)", "Монгол (Монгол)"), 9 | const Locale("en"): ("English", "English"), 10 | const Locale("it", "IT"): ("Italian (Italy)", "Italiano (Italia)"), 11 | const Locale("tr", "TR"): ("Turkish (Turkey)", "Türkçe (Türkiye)"), 12 | const Locale("fr", "FR"): ("French (France)", "Français (France)"), 13 | const Locale("de", "DE"): ("German (Germany)", "Deutsch (Deutschland)"), 14 | const Locale("ru", "RU"): ("Russian (Russia)", "Русский (Россия)"), 15 | const Locale("es", "ES"): ("Spanish (Spain)", "Español (España)"), 16 | const Locale("ar"): ("Arabic", "العربية"), 17 | }; 18 | -------------------------------------------------------------------------------- /lib/logging.dart: -------------------------------------------------------------------------------- 1 | import "package:logging/logging.dart"; 2 | 3 | final Logger mainLogger = Logger("Flow"); 4 | 5 | final Logger startupLog = Logger("Flow Startup"); 6 | final Logger themeLogger = Logger("Theme"); 7 | 8 | final Logger syncLogger = Logger("Flow Sync"); 9 | -------------------------------------------------------------------------------- /lib/prefs/theme.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/theme/color_themes/registry.dart"; 2 | import "package:local_settings/local_settings.dart"; 3 | import "package:shared_preferences/shared_preferences.dart"; 4 | 5 | class ThemeLocalPreferences { 6 | final SharedPreferencesWithCache _prefs; 7 | 8 | static ThemeLocalPreferences? _instance; 9 | 10 | factory ThemeLocalPreferences(SharedPreferences prefs) { 11 | if (_instance == null) { 12 | throw Exception( 13 | "You must initialize ThemeLocalPreferences by calling initialize().", 14 | ); 15 | } 16 | 17 | return _instance!; 18 | } 19 | 20 | late final PrimitiveSettingsEntry themeName; 21 | late final BoolSettingsEntry themeChangesAppIcon; 22 | 23 | ThemeLocalPreferences._internal(this._prefs) { 24 | SettingsEntry.defaultPrefix = "flow."; 25 | 26 | themeName = PrimitiveSettingsEntry( 27 | key: "themeName", 28 | preferences: _prefs, 29 | initialValue: flowLights.schemes.first.name, 30 | ); 31 | themeChangesAppIcon = BoolSettingsEntry( 32 | key: "themeChangesAppIcon", 33 | preferences: _prefs, 34 | initialValue: true, 35 | ); 36 | } 37 | 38 | static ThemeLocalPreferences initialize( 39 | SharedPreferencesWithCache instance, 40 | ) => _instance ??= ThemeLocalPreferences._internal(instance); 41 | } 42 | -------------------------------------------------------------------------------- /lib/routes/preferences/sections/haptics.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | import "package:flow/prefs/local_preferences.dart"; 3 | import "package:flow/routes/preferences_page.dart"; 4 | import "package:flutter/material.dart"; 5 | import "package:material_symbols_icons/symbols.dart"; 6 | 7 | class Haptics extends StatefulWidget { 8 | const Haptics({super.key}); 9 | 10 | @override 11 | State createState() => _HapticsState(); 12 | } 13 | 14 | class _HapticsState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | final bool enableHapticFeedback = LocalPreferences().enableHapticFeedback 18 | .get(); 19 | 20 | return Column( 21 | mainAxisSize: MainAxisSize.min, 22 | children: [ 23 | SwitchListTile( 24 | secondary: const Icon(Symbols.vibration_rounded), 25 | title: Text("preferences.hapticFeedback.description".t(context)), 26 | value: enableHapticFeedback, 27 | onChanged: updateEnableHapticFeedback, 28 | ), 29 | ], 30 | ); 31 | } 32 | 33 | void updateEnableHapticFeedback(bool? newEnableHapticFeedback) async { 34 | if (newEnableHapticFeedback == null) return; 35 | 36 | await LocalPreferences().enableHapticFeedback.set(newEnableHapticFeedback); 37 | 38 | if (!mounted) return; 39 | 40 | PreferencesPage.of(context).reload(); 41 | setState(() {}); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/routes/preferences/sections/privacy.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | import "package:flow/prefs/local_preferences.dart"; 3 | import "package:flow/routes/preferences_page.dart"; 4 | import "package:flutter/material.dart"; 5 | import "package:material_symbols_icons/symbols.dart"; 6 | 7 | class Privacy extends StatefulWidget { 8 | const Privacy({super.key}); 9 | 10 | @override 11 | State createState() => _PrivacyState(); 12 | } 13 | 14 | class _PrivacyState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | final bool privacyMode = LocalPreferences().privacyMode.get(); 18 | 19 | return SwitchListTile( 20 | secondary: const Icon(Symbols.password_rounded), 21 | title: Text("preferences.privacy.maskAtStartup".t(context)), 22 | value: privacyMode, 23 | onChanged: updatePrivacyMode, 24 | ); 25 | } 26 | 27 | void updatePrivacyMode(bool? newPrivacyMode) async { 28 | if (newPrivacyMode == null) return; 29 | 30 | await LocalPreferences().privacyMode.set(newPrivacyMode); 31 | 32 | if (!mounted) return; 33 | 34 | PreferencesPage.of(context).reload(); 35 | setState(() {}); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/routes/transaction_page/section.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/theme/theme.dart"; 2 | import "package:flow/widgets/general/frame.dart"; 3 | import "package:flutter/material.dart"; 4 | 5 | class Section extends StatelessWidget { 6 | final String? title; 7 | final Widget child; 8 | final Widget? titleOverride; 9 | 10 | const Section({ 11 | super.key, 12 | this.title, 13 | this.titleOverride, 14 | required this.child, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Column( 20 | mainAxisSize: MainAxisSize.min, 21 | children: [ 22 | Align( 23 | alignment: AlignmentDirectional.topStart, 24 | child: DefaultTextStyle( 25 | style: context.textTheme.labelMedium!.semi(context), 26 | child: Frame(child: titleOverride ?? Text(title ?? "A section")), 27 | ), 28 | ), 29 | child, 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/services/accounts.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/account.dart"; 2 | import "package:flow/objectbox.dart"; 3 | import "package:flow/objectbox/objectbox.g.dart"; 4 | 5 | class AccountsService { 6 | static AccountsService? _instance; 7 | 8 | factory AccountsService() => _instance ??= AccountsService._internal(); 9 | 10 | AccountsService._internal() { 11 | // Constructor 12 | } 13 | 14 | Future getOne(int id) async { 15 | return ObjectBox().box().getAsync(id); 16 | } 17 | 18 | Future> getAll() async { 19 | return ObjectBox().box().getAllAsync(); 20 | } 21 | 22 | Future findOne(dynamic identifier) async { 23 | if (identifier is int) { 24 | return await getOne(identifier); 25 | } 26 | 27 | if (identifier case String uuid) { 28 | final q = ObjectBox() 29 | .box() 30 | .query(Account_.uuid.equals(uuid)) 31 | .build(); 32 | 33 | final Account? result = await q.findFirstAsync(); 34 | 35 | q.close(); 36 | return result; 37 | } 38 | 39 | return null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/sync/exception.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/localized_exception.dart"; 2 | import "package:flow/sync/sync.dart"; 3 | 4 | class ImportException extends LocalizedException implements Exception { 5 | final String message; 6 | 7 | /// Sync Model version 8 | final int versionCode; 9 | 10 | const ImportException( 11 | this.message, { 12 | this.versionCode = latestSyncModelVersion, 13 | super.l10nKey = "error.unknown", 14 | super.l10nArgs, 15 | }); 16 | 17 | @override 18 | String toString() { 19 | return "[Flow Sync Import v$versionCode] $message"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/sync/export/export_csv/header_v1.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/named_enum.dart"; 2 | 3 | enum CSVHeader implements LocalizedEnum { 4 | uuid, 5 | title, 6 | notes, 7 | amount, 8 | currency, 9 | account, 10 | accountUuid, 11 | category, 12 | categoryUuid, 13 | type, 14 | subtype, 15 | createdDate, 16 | transactionDate, 17 | transactionDateIso8601, 18 | latitude, 19 | longitude, 20 | extra; 21 | 22 | @override 23 | String get localizationEnumValue => name; 24 | @override 25 | String get localizationEnumName => "CSVHeader"; 26 | } 27 | -------------------------------------------------------------------------------- /lib/sync/export/export_pdf/headers.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/named_enum.dart"; 2 | 3 | enum PDFHeader implements LocalizedEnum { 4 | transactionDate, 5 | title, 6 | amount, 7 | account, 8 | category; 9 | 10 | @override 11 | String get localizationEnumValue => name; 12 | @override 13 | String get localizationEnumName => "PDFHeader"; 14 | } 15 | -------------------------------------------------------------------------------- /lib/sync/export/mode.dart: -------------------------------------------------------------------------------- 1 | enum ExportMode { 2 | /// Cannot be recovered from 3 | /// 4 | /// Intended for using in more complex software like Google Sheets 5 | csv(fileExt: "csv"), 6 | 7 | /// Can be fully recovered from 8 | /// 9 | /// Intended for full backups. Will be versioned, we plan to support 10 | /// importing older backups to newer versions. 11 | json(fileExt: "json"), 12 | 13 | /// Can be fully recovered from 14 | /// 15 | /// Includes [json] inside it, plus other files like images that cannot be 16 | /// fit into a JSON file. 17 | zip(fileExt: "zip"), 18 | 19 | /// Cannot be recovered from 20 | /// 21 | /// A non-official statement (kinda like bank statements) 22 | pdf(fileExt: "pdf"); 23 | 24 | final String fileExt; 25 | 26 | const ExportMode({required this.fileExt}); 27 | 28 | static ExportMode? tryParse(String value) { 29 | switch (value.toLowerCase()) { 30 | case "csv": 31 | return ExportMode.csv; 32 | case "json": 33 | return ExportMode.json; 34 | case "zip": 35 | return ExportMode.zip; 36 | case "pdf": 37 | return ExportMode.pdf; 38 | default: 39 | return null; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/sync/import/base.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | abstract class Importer { 4 | T get data; 5 | ValueNotifier get progressNotifier; 6 | 7 | /// Before starting the import, it'll perform a safety backup, stored in 8 | /// `automated_backups` subfolder. If safety backups fails, will immediately 9 | /// halt, without making any changes to the state. You can alter this 10 | /// behaviour by setting [ignoreSafetyBackupFail] to true. 11 | /// 12 | /// Returns the path of the safety backup file. 13 | /// 14 | /// [ignoreSafetyBackupFail] - Forces to proceed in the import if safety 15 | /// backup fails 16 | Future execute({bool ignoreSafetyBackupFail = false}); 17 | } 18 | -------------------------------------------------------------------------------- /lib/sync/model/base.dart: -------------------------------------------------------------------------------- 1 | abstract class SyncModelBase { 2 | /// Version code for backup package. Useful for backwards-compatibility. 3 | /// We will increment this every time we make breaking changes to the structure. 4 | /// 5 | /// For example, let's say we changed the field name of two properties on 6 | /// [Transaction] entity. Now suddenly all the backups we made before this 7 | /// change couldn't be imported. 8 | /// 9 | /// We will ensure old data will be imported without any hassle. 10 | final int versionCode; 11 | 12 | /// Date and time of the export 13 | final DateTime exportDate; 14 | 15 | /// Current user's name 16 | final String username; 17 | 18 | /// Version of the app performed this backup 19 | final String appVersion; 20 | 21 | const SyncModelBase({ 22 | required this.versionCode, 23 | required this.exportDate, 24 | required this.username, 25 | required this.appVersion, 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /lib/sync/model/model_v1.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/account.dart"; 2 | import "package:flow/entity/category.dart"; 3 | import "package:flow/entity/transaction.dart"; 4 | import "package:flow/sync/model/base.dart"; 5 | import "package:json_annotation/json_annotation.dart"; 6 | 7 | part "model_v1.g.dart"; 8 | 9 | @JsonSerializable() 10 | class SyncModelV1 extends SyncModelBase { 11 | final List transactions; 12 | final List accounts; 13 | final List categories; 14 | 15 | const SyncModelV1({ 16 | required super.versionCode, 17 | required super.exportDate, 18 | required super.username, 19 | required super.appVersion, 20 | required this.transactions, 21 | required this.accounts, 22 | required this.categories, 23 | }); 24 | 25 | factory SyncModelV1.fromJson(Map json) => 26 | _$SyncModelV1FromJson(json); 27 | Map toJson() => _$SyncModelV1ToJson(this); 28 | } 29 | -------------------------------------------------------------------------------- /lib/sync/model/model_v1.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'model_v1.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | SyncModelV1 _$SyncModelV1FromJson(Map json) => SyncModelV1( 10 | versionCode: (json['versionCode'] as num).toInt(), 11 | exportDate: DateTime.parse(json['exportDate'] as String), 12 | username: json['username'] as String, 13 | appVersion: json['appVersion'] as String, 14 | transactions: (json['transactions'] as List) 15 | .map((e) => Transaction.fromJson(e as Map)) 16 | .toList(), 17 | accounts: (json['accounts'] as List) 18 | .map((e) => Account.fromJson(e as Map)) 19 | .toList(), 20 | categories: (json['categories'] as List) 21 | .map((e) => Category.fromJson(e as Map)) 22 | .toList(), 23 | ); 24 | 25 | Map _$SyncModelV1ToJson(SyncModelV1 instance) => 26 | { 27 | 'versionCode': instance.versionCode, 28 | 'exportDate': instance.exportDate.toIso8601String(), 29 | 'username': instance.username, 30 | 'appVersion': instance.appVersion, 31 | 'transactions': instance.transactions, 32 | 'accounts': instance.accounts, 33 | 'categories': instance.categories, 34 | }; 35 | -------------------------------------------------------------------------------- /lib/sync/sync.dart: -------------------------------------------------------------------------------- 1 | export "import.dart"; 2 | export "export.dart"; 3 | 4 | /// To be increased with everytime there's change 5 | /// in the structure of the database model. 6 | const int latestSyncModelVersion = 2; 7 | -------------------------------------------------------------------------------- /lib/theme/color_themes/catppuccin/frappe.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["Flamingo", "0xffeebebe"], 3 | ["Rosewater", "0xfff2d5cf"], 4 | ["Mauve", "0xffca9ee6"], 5 | ["Pink", "0xfff4b8e4"], 6 | ["Red", "0xffe78284"], 7 | ["Maroon", "0xffea999c"], 8 | ["Peach", "0xffef9f76"], 9 | ["Yellow", "0xffe5c890"], 10 | ["Green", "0xffa6d189"], 11 | ["Teal", "0xff81c8be"], 12 | ["Sky", "0xff99d1db"], 13 | ["Sapphire", "0xff85c1dc"], 14 | ["Blue", "0xff8caaee"], 15 | ["Lavender", "0xffbabbf1"], 16 | ["Text", "0xffc6d0f5"], 17 | ["Subtext 1", "0xffb5bfe2"], 18 | ["Subtext 0", "0xffa5adce"], 19 | ["Overlay 2", "0xff949cbb"], 20 | ["Overlay 1", "0xff838ba7"], 21 | ["Overlay 0", "0xff737994"], 22 | ["Surface 2", "0xff626880"], 23 | ["Surface 1", "0xff51576d"], 24 | ["Surface 0", "0xff414559"], 25 | ["Base", "0xff303446"], 26 | ["Mantle", "0xff292c3c"], 27 | ["Crust", "0xff232634"] 28 | ] 29 | -------------------------------------------------------------------------------- /lib/theme/color_themes/catppuccin/macchiato.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["Flamingo", "0xfff0c6c6"], 3 | ["Rosewater", "0xfff4dbd6"], 4 | ["Mauve", "0xffc6a0f6"], 5 | ["Pink", "0xfff5bde6"], 6 | ["Red", "0xffed8796"], 7 | ["Maroon", "0xffee99a0"], 8 | ["Peach", "0xfff5a97f"], 9 | ["Yellow", "0xffeed49f"], 10 | ["Green", "0xffa6da95"], 11 | ["Teal", "0xff8bd5ca"], 12 | ["Sky", "0xff91d7e3"], 13 | ["Sapphire", "0xff7dc4e4"], 14 | ["Blue", "0xff8aadf4"], 15 | ["Lavender", "0xffb7bdf8"], 16 | ["Text", "0xffcad3f5"], 17 | ["Subtext 1", "0xffb8c0e0"], 18 | ["Subtext 0", "0xffa5adcb"], 19 | ["Overlay 2", "0xff939ab7"], 20 | ["Overlay 1", "0xff8087a2"], 21 | ["Overlay 0", "0xff6e738d"], 22 | ["Surface 2", "0xff5b6078"], 23 | ["Surface 1", "0xff494d64"], 24 | ["Surface 0", "0xff363a4f"], 25 | ["Base", "0xff24273a"], 26 | ["Mantle", "0xff1e2030"], 27 | ["Crust", "0xff181926"] 28 | ] 29 | -------------------------------------------------------------------------------- /lib/theme/color_themes/catppuccin/mocha.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["Flamingo", "0xfff2cdcd"], 3 | ["Rosewater", "0xfff5e0dc"], 4 | ["Mauve", "0xffcba6f7"], 5 | ["Pink", "0xfff5c2e7"], 6 | ["Red", "0xfff38ba8"], 7 | ["Maroon", "0xffeba0ac"], 8 | ["Peach", "0xfffab387"], 9 | ["Yellow", "0xfff9e2af"], 10 | ["Green", "0xffa6e3a1"], 11 | ["Teal", "0xff94e2d5"], 12 | ["Sky", "0xff89dceb"], 13 | ["Sapphire", "0xff74c7ec"], 14 | ["Blue", "0xff89b4fa"], 15 | ["Lavender", "0xffb4befe"], 16 | ["Text", "0xffcdd6f4"], 17 | ["Subtext 1", "0xffbac2de"], 18 | ["Subtext 0", "0xffa6adc8"], 19 | ["Overlay 2", "0xff9399b2"], 20 | ["Overlay 1", "0xff7f849c"], 21 | ["Overlay 0", "0xff6c7086"], 22 | ["Surface 2", "0xff585b70"], 23 | ["Surface 1", "0xff45475a"], 24 | ["Surface 0", "0xff313244"], 25 | ["Base", "0xff1e1e2e"], 26 | ["Mantle", "0xff181825"], 27 | ["Crust", "0xff11111b"] 28 | ] 29 | -------------------------------------------------------------------------------- /lib/theme/color_themes/palenight.dart: -------------------------------------------------------------------------------- 1 | import "dart:ui"; 2 | 3 | import "package:flow/theme/flow_color_scheme.dart"; 4 | 5 | final FlowColorScheme palenight = FlowColorScheme( 6 | name: "palenight", 7 | isDark: true, 8 | surface: const Color(0xff292D3E), 9 | onSurface: const Color(0xfff5f6fa), 10 | primary: const Color(0xfff5f6fa), 11 | onPrimary: const Color(0xff444267), 12 | secondary: const Color(0xff202331), 13 | onSecondary: const Color(0xfff5f6fa), 14 | customColors: FlowCustomColors( 15 | income: Color(0xFFc3e88d), 16 | expense: Color(0xFFf07178), 17 | semi: Color(0xFF676E95), 18 | ), 19 | ); 20 | -------------------------------------------------------------------------------- /lib/theme/flow_custom_colors.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | class FlowCustomColors extends ThemeExtension { 4 | /// Color for income 5 | final Color income; 6 | 7 | /// Color for expense 8 | final Color expense; 9 | 10 | /// Color for labels, secondary body texts 11 | final Color semi; 12 | 13 | const FlowCustomColors({ 14 | required this.income, 15 | required this.expense, 16 | required this.semi, 17 | }); 18 | 19 | @override 20 | FlowCustomColors copyWith({Color? income, Color? expense, Color? semi}) { 21 | return FlowCustomColors( 22 | income: income ?? this.income, 23 | expense: expense ?? this.expense, 24 | semi: semi ?? this.semi, 25 | ); 26 | } 27 | 28 | @override 29 | FlowCustomColors lerp(FlowCustomColors? other, double t) { 30 | if (other is! FlowCustomColors) return this; 31 | 32 | return FlowCustomColors( 33 | income: Color.lerp(income, other.income, t)!, 34 | expense: Color.lerp(expense, other.expense, t)!, 35 | semi: Color.lerp(semi, other.semi, t)!, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/theme/flow_theme_group.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/theme/flow_color_scheme.dart"; 3 | 4 | class FlowThemeGroup { 5 | final String name; 6 | final FlowIconData? icon; 7 | 8 | final List schemes; 9 | 10 | const FlowThemeGroup({required this.schemes, required this.name, this.icon}); 11 | 12 | Map get schemesMap { 13 | final Map map = {}; 14 | 15 | for (final FlowColorScheme scheme in schemes) { 16 | map[scheme.name] = scheme; 17 | } 18 | 19 | return map; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/theme/pie_theme_extension.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/logging.dart"; 2 | import "package:flutter/material.dart"; 3 | import "package:pie_menu/pie_menu.dart"; 4 | 5 | class PieThemeExtension extends ThemeExtension { 6 | final PieTheme pieTheme; 7 | 8 | const PieThemeExtension({required this.pieTheme}); 9 | 10 | @override 11 | ThemeExtension copyWith({PieTheme? pieTheme}) { 12 | return PieThemeExtension(pieTheme: pieTheme ?? this.pieTheme); 13 | } 14 | 15 | @override 16 | ThemeExtension lerp( 17 | ThemeExtension? other, 18 | double t, 19 | ) { 20 | mainLogger.warning( 21 | "[PieThemeExtension] lerp is not available for PieTheme", 22 | ); 23 | 24 | return (t < 0.5 ? this : other) as PieThemeExtension; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/utils/csv_parser.dart: -------------------------------------------------------------------------------- 1 | import "dart:convert"; 2 | import "dart:io"; 3 | import "dart:typed_data"; 4 | import "package:charset/charset.dart"; 5 | import "package:csv/csv.dart"; 6 | import "package:flow/utils/line_break_normalizer.dart"; 7 | 8 | Future> parseCsvFromFile(File file) async { 9 | final Uint8List bytes = file.readAsBytesSync(); 10 | 11 | String? parsed; 12 | 13 | try { 14 | parsed = utf8.decode(bytes); 15 | } catch (e) { 16 | // Silent fail 17 | } 18 | 19 | if (parsed == null) { 20 | try { 21 | parsed = utf16.decode(bytes); 22 | } catch (e) { 23 | // Silent fail 24 | } 25 | } 26 | 27 | if (parsed == null) { 28 | try { 29 | parsed = utf32.decode(bytes); 30 | } catch (e) { 31 | // Silent fail 32 | } 33 | } 34 | 35 | if (parsed == null) { 36 | try { 37 | parsed = latin1.decode(bytes); 38 | } catch (e) { 39 | // Silent fail 40 | } 41 | } 42 | 43 | if (parsed == null) { 44 | throw Exception( 45 | "Unsupported text encoding. Please provide a CSV with one of following encodings: ascii, utf8, utf16, utf32, latin1", 46 | ); 47 | } 48 | 49 | final String lineBreaksNormalized = LineBreakNormalizer.normalize(parsed); 50 | 51 | return CsvToListConverter( 52 | eol: LineBreakNormalizer.terminator, 53 | ).convert(lineBreaksNormalized); 54 | } 55 | -------------------------------------------------------------------------------- /lib/utils/extensions.dart: -------------------------------------------------------------------------------- 1 | export "extensions/custom_popups.dart"; 2 | export "extensions/go_router.dart"; 3 | export "extensions/iterables.dart"; 4 | export "extensions/num.dart"; 5 | export "extensions/string.dart"; 6 | export "extensions/toast.dart"; 7 | export "extensions/transaction.dart"; 8 | export "extensions/transaction_filter.dart"; 9 | -------------------------------------------------------------------------------- /lib/utils/extensions/directionality.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | extension ContextDirectionality on BuildContext { 4 | bool get isRtl => Directionality.of(this) == TextDirection.rtl; 5 | bool get isLtr => Directionality.of(this) == TextDirection.ltr; 6 | } 7 | -------------------------------------------------------------------------------- /lib/utils/extensions/go_router.dart: -------------------------------------------------------------------------------- 1 | // Ongoing issue about lack of `popUntil` 2 | // https://github.com/flutter/flutter/issues/131625 3 | import "package:go_router/go_router.dart"; 4 | 5 | extension GoRouterExt on GoRouter { 6 | void popUntil(bool Function(GoRoute) predicate) { 7 | List routeStacks = [...routerDelegate.currentConfiguration.routes]; 8 | 9 | for (int i = routeStacks.length - 1; i >= 0; i--) { 10 | RouteBase route = routeStacks[i]; 11 | if (route is GoRoute) { 12 | if (predicate(route)) break; 13 | if (i != 0 && routeStacks[i - 1] is ShellRoute) { 14 | RouteMatchList matchList = routerDelegate.currentConfiguration; 15 | restore(matchList.remove(matchList.matches.last)); 16 | } else { 17 | pop(); 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/utils/extensions/interval_report.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | import "package:flow/reports/interval_flow_report.dart"; 3 | import "package:flutter/material.dart"; 4 | 5 | extension IntervalReportL10n on IntervalFlowReport { 6 | String averageTitle(BuildContext context) { 7 | if (interval == const Duration(hours: 1)) { 8 | return "tabs.stats.intervalReport.averages@hour".t(context); 9 | } else if (interval == const Duration(days: 1)) { 10 | return "tabs.stats.intervalReport.averages@day".t(context); 11 | } else if (interval == const Duration(days: 7)) { 12 | return "tabs.stats.intervalReport.averages@week".t(context); 13 | } else if (interval == const Duration(days: 30)) { 14 | return "tabs.stats.intervalReport.averages@month".t(context); 15 | } else if (interval == const Duration(days: 365)) { 16 | return "tabs.stats.intervalReport.averages@year".t(context); 17 | } else { 18 | return "avg, per (${interval.inSeconds} seconds)".t(context); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/utils/extensions/num.dart: -------------------------------------------------------------------------------- 1 | import "dart:math"; 2 | 3 | extension NumberFormatter on num { 4 | /// Returns string with [decimalPlaces] decimal places. 5 | /// 6 | /// Example: 7 | /// `0.69.toStringAsFixed(2) => "69.00%"` 8 | String percent([int decimalPlaces = 1]) { 9 | return "${(this * 100).toStringAsFixed(decimalPlaces)}%"; 10 | } 11 | 12 | /// No decimal places. 13 | /// 14 | /// Example: 15 | /// `0.69.percent2 => "69%"` 16 | String get percentInt => percent(0); 17 | 18 | /// One decimal places. 19 | /// 20 | /// Example: 21 | /// `0.691.percent2 => "69.1%"` 22 | String get percent1 => percent(1); 23 | 24 | /// Two decimal places. 25 | /// 26 | /// Example: 27 | /// `0.42.percent2 => "42.00%"` 28 | String get percent2 => percent(2); 29 | 30 | String get humanReadableBinarySize { 31 | const log1024 = 6.931471805599453; 32 | const formats = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; 33 | 34 | final int unitIndex = (log(toDouble()) / log1024).floor(); 35 | 36 | return "${(this / pow(1024, unitIndex)).toStringAsFixed(1)} ${formats[unitIndex]}"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/utils/extensions/recurring_transaction.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/recurring_transaction.dart"; 2 | import "package:flow/entity/transaction/extensions/default/recurring.dart"; 3 | 4 | extension RecurringTransactionHelpers on RecurringTransaction { 5 | String get extensionIdentifierTag => Recurring( 6 | uuid: uuid, 7 | initialTransactionDate: DateTime.now(), 8 | ).extensionIdentifierTag; 9 | } 10 | -------------------------------------------------------------------------------- /lib/utils/extensions/string.dart: -------------------------------------------------------------------------------- 1 | extension Casings on String { 2 | static RegExp whitespaceMatcher = RegExp(r"\s"); 3 | 4 | static List titleCaseLowercaseWords = [ 5 | "a", 6 | "an", 7 | "the", 8 | "at", 9 | "by", 10 | "for", 11 | "in", 12 | "of", 13 | "on", 14 | "to", 15 | "up", 16 | "and", 17 | "as", 18 | "but", 19 | "or", 20 | "nor", 21 | ]; 22 | 23 | String capitalize() { 24 | if (isEmpty) return this; 25 | 26 | return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; 27 | } 28 | 29 | /// Does not preserve original whitespace characters. 30 | /// 31 | /// All whitespace will be replaced with a single space. 32 | String titleCase() { 33 | if (isEmpty) return this; 34 | 35 | return split(whitespaceMatcher) 36 | .map( 37 | (e) => titleCaseLowercaseWords.contains(e.toLowerCase()) 38 | ? e.toLowerCase() 39 | : e.capitalize(), 40 | ) 41 | .join(" "); 42 | } 43 | 44 | String get digitsObscured => replaceAll(RegExp(r"\d"), "*"); 45 | 46 | /// Removes leading zeroes from a string. 47 | /// 48 | /// e.g., 49 | /// 0a -> a 50 | /// 02 -> 2 51 | /// 03xe -> 3xe 52 | String get withoutLeadingZeroes => replaceAll(RegExp(r"^0*"), ""); 53 | } 54 | -------------------------------------------------------------------------------- /lib/utils/guess_preset_icon.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/data/setup/default_accounts.dart"; 3 | import "package:flow/data/setup/default_categories.dart"; 4 | import "package:flow/entity/account.dart"; 5 | import "package:flow/entity/category.dart"; 6 | import "package:material_symbols_icons/symbols.dart"; 7 | 8 | /// Falls back to [fallback] 9 | FlowIconData guessPresetIcon( 10 | String name, { 11 | FlowIconData fallback = const IconFlowIcon(Symbols.circle_rounded), 12 | }) { 13 | name = name.trim().toLowerCase(); 14 | 15 | for (final Account accountPreset in getAccountPresets("USD")) { 16 | if (accountPreset.name.toLowerCase() == name) { 17 | return accountPreset.icon; 18 | } 19 | } 20 | 21 | for (final Category categoryPreset in getCategoryPresets()) { 22 | if (categoryPreset.name.toLowerCase() == name) { 23 | return categoryPreset.icon; 24 | } 25 | } 26 | 27 | return fallback; 28 | } 29 | -------------------------------------------------------------------------------- /lib/utils/is_desktop.dart: -------------------------------------------------------------------------------- 1 | import "dart:io"; 2 | 3 | bool isDesktop() { 4 | return Platform.isWindows || Platform.isMacOS || Platform.isLinux; 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils/jasonable.dart: -------------------------------------------------------------------------------- 1 | abstract class Jasonable { 2 | Map toJson(); 3 | } 4 | -------------------------------------------------------------------------------- /lib/utils/json/time_range_converter.dart: -------------------------------------------------------------------------------- 1 | import "package:json_annotation/json_annotation.dart"; 2 | import "package:moment_dart/moment_dart.dart"; 3 | 4 | class TimeRangeConverter implements JsonConverter { 5 | const TimeRangeConverter(); 6 | 7 | @override 8 | TimeRange fromJson(String serialized) { 9 | return TimeRange.parse(serialized); 10 | } 11 | 12 | @override 13 | String toJson(TimeRange range) { 14 | return range.encodeShort(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/utils/json/utc_datetime_converter.dart: -------------------------------------------------------------------------------- 1 | import "package:json_annotation/json_annotation.dart"; 2 | 3 | class UTCDateTimeConverter implements JsonConverter { 4 | const UTCDateTimeConverter(); 5 | 6 | @override 7 | DateTime fromJson(String dateTime) { 8 | return DateTime.parse(dateTime).toLocal(); 9 | } 10 | 11 | @override 12 | String toJson(DateTime dateTime) { 13 | return dateTime.toUtc().toIso8601String(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/utils/line_break_normalizer.dart: -------------------------------------------------------------------------------- 1 | import "dart:convert"; 2 | 3 | /// Converts all line breaks to [terminator], and get rid of empty lines. 4 | class LineBreakNormalizer extends Converter { 5 | static const String terminator = "\n"; 6 | 7 | const LineBreakNormalizer(); 8 | 9 | @override 10 | String convert(String input) => normalize(input); 11 | 12 | @override 13 | Sink startChunkedConversion(Sink sink) { 14 | return LineBreakNormalizerSink(sink); 15 | } 16 | 17 | static String normalize(String input) => input 18 | .replaceAll("\r\n", terminator) 19 | .replaceAll("\r", terminator) 20 | .replaceAll(RegExp(r"\s+$", multiLine: true), ""); 21 | } 22 | 23 | class LineBreakNormalizerSink implements ChunkedConversionSink { 24 | final Sink _sink; 25 | 26 | LineBreakNormalizerSink(this._sink); 27 | 28 | @override 29 | void close() { 30 | _sink.close(); 31 | } 32 | 33 | @override 34 | void add(String chunk) { 35 | _sink.add(LineBreakNormalizer.normalize(chunk)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/utils/number_formatting.dart: -------------------------------------------------------------------------------- 1 | import "package:intl/intl.dart"; 2 | 3 | String getDecimalSeparatorForCurrency(String? currency) { 4 | return currency == null 5 | ? "." 6 | : NumberFormat.simpleCurrency(name: currency).symbols.DECIMAL_SEP; 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils/open_url.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/logging.dart"; 2 | import "package:url_launcher/url_launcher.dart"; 3 | 4 | Future openUrl( 5 | Uri uri, [ 6 | LaunchMode mode = LaunchMode.externalApplication, 7 | ]) async { 8 | try { 9 | return await launchUrl(uri); 10 | } catch (e) { 11 | mainLogger.warning("Failed to launch uri ($uri) due to $e"); 12 | return false; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/utils/optional.dart: -------------------------------------------------------------------------------- 1 | class Optional { 2 | final T? value; 3 | 4 | const Optional(this.value); 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils/shortcut.dart: -------------------------------------------------------------------------------- 1 | import "dart:io"; 2 | 3 | import "package:flutter/material.dart"; 4 | import "package:flutter/services.dart"; 5 | import "package:flutter/widgets.dart"; 6 | 7 | bool _shouldUseMeta() => Platform.isMacOS || Platform.isIOS; 8 | 9 | /// A SingleActivator that gets triggered if [key] is pressed with 10 | /// * `Meta` for macOS and iOS (iPadOS) 11 | /// * `Control` for other platforms 12 | /// 13 | /// It's also possible to pass [shift] and [alt] to the constructor. 14 | SingleActivator osSingleActivator( 15 | LogicalKeyboardKey key, [ 16 | bool shift = false, 17 | bool alt = false, 18 | ]) { 19 | final meta = _shouldUseMeta(); 20 | final control = !meta; 21 | 22 | return SingleActivator( 23 | key, 24 | control: control, 25 | meta: meta, 26 | shift: shift, 27 | alt: alt, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /lib/utils/simple_query_sorter.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/currencies.dart"; 2 | import "package:flow/entity/account.dart"; 3 | import "package:flow/entity/category.dart"; 4 | import "package:fuzzywuzzy/fuzzywuzzy.dart"; 5 | 6 | List simpleSortByQuery(List items, String query) { 7 | final String normalizedQuery = query.trim().toLowerCase(); 8 | 9 | if (normalizedQuery.isEmpty) return items; 10 | 11 | return extractAllSorted( 12 | query: normalizedQuery, 13 | choices: items, 14 | getter: (item) { 15 | if (item case Category category) { 16 | return category.name; 17 | } 18 | 19 | if (item case Account account) { 20 | return account.name; 21 | } 22 | 23 | if (item case CurrencyData currencyData) { 24 | return [ 25 | currencyData.code, 26 | currencyData.name, 27 | currencyData.country, 28 | ].join(" "); 29 | } 30 | 31 | return item.toString(); 32 | }, 33 | ).map((result) => result.choice).toList(); 34 | } 35 | -------------------------------------------------------------------------------- /lib/utils/utils.dart: -------------------------------------------------------------------------------- 1 | export "csv_parser.dart"; 2 | export "extensions.dart"; 3 | export "is_desktop.dart"; 4 | export "jasonable.dart"; 5 | export "line_break_normalizer.dart"; 6 | export "number_formatting.dart"; 7 | export "open_url.dart"; 8 | export "optional.dart"; 9 | export "pick_file.dart"; 10 | export "shortcut.dart"; 11 | export "simple_query_sorter.dart"; 12 | -------------------------------------------------------------------------------- /lib/widgets/add_category_card.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/l10n/extensions.dart"; 3 | import "package:flow/widgets/general/flow_icon.dart"; 4 | import "package:flow/widgets/general/surface.dart"; 5 | import "package:flutter/material.dart"; 6 | import "package:go_router/go_router.dart"; 7 | import "package:material_symbols_icons/symbols.dart"; 8 | 9 | class AddCategoryCard extends StatelessWidget { 10 | final VoidCallback? onTapOverride; 11 | 12 | const AddCategoryCard({super.key, this.onTapOverride}); 13 | 14 | static BorderRadius borderRadius = BorderRadius.circular(16.0); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Surface( 19 | shape: RoundedRectangleBorder(borderRadius: borderRadius), 20 | builder: (context) => InkWell( 21 | borderRadius: borderRadius, 22 | onTap: onTapOverride ?? (() => context.push("/category/new")), 23 | child: Row( 24 | children: [ 25 | FlowIcon( 26 | FlowIconData.icon(Symbols.add_rounded), 27 | size: 32.0, 28 | plated: true, 29 | ), 30 | const SizedBox(width: 12.0), 31 | Column( 32 | mainAxisSize: MainAxisSize.min, 33 | crossAxisAlignment: CrossAxisAlignment.start, 34 | children: [Text("category.new".t(context))], 35 | ), 36 | const Spacer(), 37 | ], 38 | ), 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/widgets/chart_legend.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/theme/theme.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | class ChartLegend extends StatelessWidget { 5 | final Color color; 6 | final String label; 7 | 8 | const ChartLegend({super.key, required this.color, required this.label}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Row( 13 | mainAxisSize: MainAxisSize.min, 14 | children: [ 15 | Container( 16 | width: 16.0, 17 | height: 16.0, 18 | decoration: BoxDecoration( 19 | color: color, 20 | borderRadius: BorderRadius.circular(4.0), 21 | border: Border.all( 22 | color: context.colorScheme.onSurface, 23 | width: 1.0, 24 | ), 25 | ), 26 | ), 27 | const SizedBox(width: 8.0), 28 | Text(label, style: context.textTheme.bodyMedium), 29 | ], 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/widgets/export/export_history/no_backups.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/l10n/extensions.dart"; 3 | import "package:flow/theme/theme.dart"; 4 | import "package:flow/widgets/general/flow_icon.dart"; 5 | import "package:flutter/material.dart"; 6 | import "package:material_symbols_icons/symbols.dart"; 7 | 8 | class NoBackups extends StatelessWidget { 9 | const NoBackups({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Padding( 14 | padding: const EdgeInsets.all(24.0), 15 | child: Center( 16 | child: Column( 17 | mainAxisSize: MainAxisSize.min, 18 | children: [ 19 | Text( 20 | "sync.export.history.empty".t(context), 21 | textAlign: TextAlign.center, 22 | style: context.textTheme.headlineMedium, 23 | ), 24 | const SizedBox(height: 8.0), 25 | FlowIcon( 26 | FlowIconData.icon(Symbols.history_rounded), 27 | size: 128.0, 28 | color: context.colorScheme.primary, 29 | ), 30 | const SizedBox(height: 8.0), 31 | Text( 32 | "sync.export.history.empty.description".t(context), 33 | textAlign: TextAlign.center, 34 | ), 35 | ], 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/widgets/general/blur_backgorund.dart: -------------------------------------------------------------------------------- 1 | import "dart:ui"; 2 | 3 | import "package:flutter/material.dart"; 4 | 5 | class BlurBackground extends StatelessWidget { 6 | final bool blur; 7 | final Widget child; 8 | 9 | final double sigmaX; 10 | final double sigmaY; 11 | 12 | const BlurBackground({ 13 | super.key, 14 | required this.blur, 15 | required this.child, 16 | this.sigmaX = 2.0, 17 | this.sigmaY = 2.0, 18 | }); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return ClipRect( 23 | child: Stack( 24 | children: [ 25 | child, 26 | if (blur) 27 | Positioned.fill( 28 | child: BackdropFilter( 29 | filter: ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY), 30 | child: Container(), 31 | ), 32 | ), 33 | ], 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/widgets/general/directional_chevron.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/widgets/general/rtl_flipper.dart"; 2 | import "package:flutter/material.dart"; 3 | import "package:material_symbols_icons/symbols.dart"; 4 | 5 | class DirectionalChevron extends StatelessWidget { 6 | const DirectionalChevron({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return const RTLFlipper(child: Icon(Symbols.chevron_right_rounded)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/widgets/general/frame.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/theme/theme.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | class Frame extends StatelessWidget { 5 | final bool pad; 6 | final EdgeInsets padding; 7 | final Widget child; 8 | final bool withSurface; 9 | 10 | const Frame({ 11 | super.key, 12 | this.pad = true, 13 | this.withSurface = false, 14 | this.padding = const EdgeInsets.symmetric(horizontal: 16.0), 15 | required this.child, 16 | }); 17 | 18 | const Frame.standalone({ 19 | super.key, 20 | this.withSurface = false, 21 | required this.child, 22 | }) : pad = true, 23 | padding = const EdgeInsets.all(16.0); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Container( 28 | color: withSurface ? context.colorScheme.surface : null, 29 | padding: pad ? padding : EdgeInsets.zero, 30 | child: child, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/widgets/general/info_text.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/theme/theme.dart"; 2 | import "package:flutter/material.dart"; 3 | import "package:material_symbols_icons/symbols.dart"; 4 | 5 | class InfoText extends StatelessWidget { 6 | final Widget child; 7 | 8 | final IconData icon; 9 | 10 | const InfoText({ 11 | super.key, 12 | required this.child, 13 | this.icon = Symbols.info_rounded, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Row( 19 | crossAxisAlignment: CrossAxisAlignment.center, 20 | children: [ 21 | Icon(icon, fill: 0, color: context.flowColors.semi, size: 16.0), 22 | const SizedBox(width: 4.0), 23 | Flexible( 24 | child: DefaultTextStyle( 25 | style: context.textTheme.bodySmall!.semi(context), 26 | child: child, 27 | ), 28 | ), 29 | ], 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/widgets/general/list_header.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/theme/theme.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | class ListHeader extends StatelessWidget { 5 | final String title; 6 | final EdgeInsets padding; 7 | 8 | final TextStyle? style; 9 | 10 | const ListHeader( 11 | this.title, { 12 | super.key, 13 | this.style, 14 | this.padding = const EdgeInsets.symmetric(horizontal: 16.0), 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Padding( 20 | padding: padding, 21 | child: Text(title, style: style ?? context.textTheme.titleSmall), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/widgets/general/map.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/widgets/general/modal_overflow_bar.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | class ModalOverflowBar extends StatelessWidget { 4 | final EdgeInsets padding; 5 | final List children; 6 | 7 | final double spacing; 8 | final MainAxisAlignment? alignment; 9 | final double overflowSpacing; 10 | final OverflowBarAlignment overflowAlignment; 11 | final VerticalDirection overflowDirection; 12 | final TextDirection? textDirection; 13 | 14 | const ModalOverflowBar({ 15 | super.key, 16 | this.padding = const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), 17 | required this.children, 18 | this.alignment, 19 | this.spacing = 12.0, 20 | this.overflowSpacing = 12.0, 21 | this.overflowAlignment = OverflowBarAlignment.start, 22 | this.overflowDirection = VerticalDirection.down, 23 | this.textDirection, 24 | }); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Padding( 29 | padding: padding, 30 | child: OverflowBar( 31 | alignment: alignment, 32 | spacing: spacing, 33 | overflowSpacing: overflowSpacing, 34 | overflowAlignment: overflowAlignment, 35 | overflowDirection: overflowDirection, 36 | textDirection: textDirection, 37 | children: children, 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/widgets/general/rtl_flipper.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/utils/extensions/directionality.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | class RTLFlipper extends StatelessWidget { 5 | final Widget child; 6 | 7 | const RTLFlipper({super.key, required this.child}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Transform.flip(flipX: context.isRtl, child: child); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/widgets/general/spinner.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | /// Indefinite waiting indicator 4 | class Spinner extends StatelessWidget { 5 | final bool center; 6 | 7 | const Spinner({super.key, this.center = false}); 8 | const Spinner.center({super.key}) : center = true; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | const child = CircularProgressIndicator(); 13 | 14 | if (center) { 15 | return const Center(child: child); 16 | } 17 | 18 | return child; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/widgets/general/wavy_divider.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/widgets/general/wavy_divider/wavy_divider_painter.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | class WavyDivider extends StatelessWidget { 5 | /// Height of the divider 6 | /// 7 | /// Visually, will be less than [height] due to usage of bezier curves 8 | final double height; 9 | 10 | /// Width of a single wave 11 | final double waveWidth; 12 | 13 | /// Color of the divider, defaults to theme's `dividerColor` 14 | final Color? color; 15 | 16 | /// Width of the stroke 17 | final double strokeWidth; 18 | 19 | const WavyDivider({ 20 | super.key, 21 | this.height = 16.0, 22 | this.waveWidth = 16.0, 23 | this.strokeWidth = 2.0, 24 | this.color, 25 | }); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | final Color color = this.color ?? Theme.of(context).dividerColor; 30 | 31 | return SizedBox( 32 | width: double.infinity, 33 | height: height, 34 | child: ClipRect( 35 | child: CustomPaint( 36 | key: ValueKey(color), 37 | painter: WavyDividerPainter( 38 | color: color, 39 | height: height, 40 | waveWidth: waveWidth, 41 | strokeWidth: strokeWidth, 42 | ), 43 | ), 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/widgets/general/wavy_divider/wavy_divider_painter.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | class WavyDividerPainter extends CustomPainter { 4 | /// Width of single semi-circle 5 | final double waveWidth; 6 | 7 | /// Height of the divider 8 | final double height; 9 | 10 | final Color color; 11 | 12 | final double strokeWidth; 13 | 14 | const WavyDividerPainter({ 15 | super.repaint, 16 | required this.color, 17 | required this.height, 18 | required this.waveWidth, 19 | required this.strokeWidth, 20 | }); 21 | 22 | @override 23 | void paint(Canvas canvas, Size size) { 24 | final paint = Paint() 25 | ..color = color 26 | ..strokeWidth = strokeWidth 27 | ..style = PaintingStyle.stroke 28 | ..strokeCap = StrokeCap.round; 29 | 30 | final double halfHeight = height * 0.5; 31 | 32 | final Path path = Path()..moveTo(0, halfHeight); 33 | 34 | final int iterations = (size.width / waveWidth).ceil(); 35 | 36 | for (int i = 0; i < iterations; i++) { 37 | path.relativeQuadraticBezierTo( 38 | waveWidth * 0.5, 39 | halfHeight * (i % 2 == 0 ? 1 : -1), 40 | waveWidth, 41 | 0, 42 | ); 43 | } 44 | 45 | canvas.drawPath(path, paint); 46 | } 47 | 48 | @override 49 | bool shouldRepaint(WavyDividerPainter oldDelegate) => false; 50 | } 51 | -------------------------------------------------------------------------------- /lib/widgets/home/preferences/button_order_preferences/transaction_type_button.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/transaction.dart"; 2 | import "package:flow/theme/theme.dart"; 3 | import "package:flutter/material.dart"; 4 | 5 | class TransactionTypeButton extends StatelessWidget { 6 | final double opacity; 7 | final TransactionType type; 8 | 9 | const TransactionTypeButton({ 10 | super.key, 11 | required this.type, 12 | this.opacity = 1.0, 13 | }); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final Widget child = Container( 18 | decoration: BoxDecoration( 19 | shape: BoxShape.circle, 20 | color: type.actionBackgroundColor(context), 21 | ), 22 | padding: const EdgeInsets.all(16.0), 23 | child: Icon(type.icon, color: type.actionColor(context), weight: 800.0), 24 | ); 25 | 26 | if (opacity == 1.0) { 27 | return child; 28 | } 29 | 30 | return Opacity(opacity: opacity, child: child); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/widgets/home/preferences/transfer_preferences/demo_transaction_list_tile.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/entity/transaction.dart"; 3 | import "package:flow/theme/theme.dart"; 4 | import "package:flow/widgets/general/flow_icon.dart"; 5 | import "package:flutter/material.dart"; 6 | import "package:material_symbols_icons/symbols.dart"; 7 | 8 | class DemoTransactionListTile extends StatelessWidget { 9 | final TransactionType type; 10 | 11 | const DemoTransactionListTile({super.key, required this.type}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final IconData icon = switch (type) { 16 | TransactionType.transfer => Symbols.sync_alt_rounded, 17 | _ => Symbols.circle_rounded, 18 | }; 19 | 20 | return Row( 21 | children: [ 22 | FlowIcon(FlowIconData.icon(icon), plated: true, fill: 0.0), 23 | const SizedBox(width: 4.0), 24 | Text("▅▅▅", style: context.textTheme.bodySmall?.semi(context)), 25 | const Spacer(), 26 | Text( 27 | type == TransactionType.expense ? "-▇" : "▇", 28 | style: context.textTheme.bodyLarge?.copyWith( 29 | color: type.color(context), 30 | fontWeight: FontWeight.bold, 31 | ), 32 | ), 33 | ], 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/widgets/home/privacy_toggler.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/prefs/transitive.dart"; 2 | import "package:flutter/material.dart"; 3 | import "package:material_symbols_icons/symbols.dart"; 4 | 5 | class PrivacyToggler extends StatelessWidget { 6 | const PrivacyToggler({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return ValueListenableBuilder( 11 | valueListenable: 12 | TransitiveLocalPreferences().sessionPrivacyMode.valueNotifier, 13 | builder: (context, snapshot, _) { 14 | final bool obscure = snapshot == true; 15 | 16 | return IconButton( 17 | onPressed: () => 18 | TransitiveLocalPreferences().sessionPrivacyMode.set(!obscure), 19 | icon: Icon( 20 | obscure 21 | ? Symbols.visibility_rounded 22 | : Symbols.visibility_off_rounded, 23 | ), 24 | ); 25 | }, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/widgets/home/stats/info_card_with_delta.dart: -------------------------------------------------------------------------------- 1 | import "package:auto_size_text/auto_size_text.dart"; 2 | import "package:flow/data/money.dart"; 3 | import "package:flow/prefs/local_preferences.dart"; 4 | import "package:flow/theme/theme.dart"; 5 | import "package:flow/widgets/general/money_text.dart"; 6 | import "package:flow/widgets/home/home/info_card.dart"; 7 | import "package:flow/widgets/trend.dart"; 8 | import "package:flutter/material.dart"; 9 | 10 | class InfoCardWithDelta extends StatelessWidget { 11 | final Money money; 12 | final Money? previousMoney; 13 | 14 | final bool invertDelta; 15 | 16 | final AutoSizeGroup? autoSizeGroup; 17 | 18 | final String title; 19 | 20 | const InfoCardWithDelta({ 21 | super.key, 22 | required this.money, 23 | required this.previousMoney, 24 | required this.autoSizeGroup, 25 | required this.title, 26 | this.invertDelta = false, 27 | }); 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return InfoCard( 32 | title: title, 33 | money: MoneyText( 34 | money, 35 | tapToToggleAbbreviation: true, 36 | initiallyAbbreviated: !LocalPreferences().preferFullAmounts.get(), 37 | autoSize: true, 38 | autoSizeGroup: autoSizeGroup, 39 | style: context.textTheme.displaySmall, 40 | ), 41 | delta: Trend.fromMoney( 42 | current: money, 43 | previous: previousMoney, 44 | invertDelta: invertDelta, 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/widgets/import_wizard/backup_info.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/sync/import/base.dart"; 2 | import "package:flow/sync/import/external/ivy_wallet_csv.dart"; 3 | import "package:flow/sync/import/import_csv.dart"; 4 | import "package:flow/sync/import/import_v1.dart"; 5 | import "package:flow/sync/import/import_v2.dart"; 6 | import "package:flow/widgets/import_wizard/csv/backup_info_csv.dart"; 7 | import "package:flow/widgets/import_wizard/ivy_wallet/backup_info.dart"; 8 | import "package:flow/widgets/import_wizard/v1/backup_info_v1.dart"; 9 | import "package:flow/widgets/import_wizard/v2/backup_info_v2.dart"; 10 | import "package:flutter/widgets.dart"; 11 | 12 | class BackupInfo extends StatelessWidget { 13 | final Importer importer; 14 | final VoidCallback onClickStart; 15 | 16 | const BackupInfo({ 17 | super.key, 18 | required this.importer, 19 | required this.onClickStart, 20 | }); 21 | 22 | @override 23 | Widget build(BuildContext context) => switch (importer) { 24 | ImportV1 v1 => BackupInfoV1(importer: v1, onClickStart: onClickStart), 25 | ImportV2 v2 => BackupInfoV2(importer: v2, onClickStart: onClickStart), 26 | ImportCSV csv => BackupInfoCSV(importer: csv, onClickStart: onClickStart), 27 | IvyWalletCsvImporter ivyImporter => BackupInfoIvyWalletCsv( 28 | importer: ivyImporter, 29 | onClickStart: onClickStart, 30 | ), 31 | _ => Container(), 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /lib/widgets/import_wizard/csv/account_currency_list_tile.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/theme/helpers.dart"; 2 | import "package:flutter/material.dart"; 3 | import "package:material_symbols_icons/symbols.dart"; 4 | 5 | class AccountCurrencyListTile extends StatelessWidget { 6 | final String name; 7 | final String? currency; 8 | 9 | final VoidCallback? onTap; 10 | 11 | const AccountCurrencyListTile({ 12 | super.key, 13 | required this.name, 14 | this.currency, 15 | this.onTap, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return ListTile( 21 | title: Text(name), 22 | trailing: Row( 23 | mainAxisSize: MainAxisSize.min, 24 | children: [ 25 | Text( 26 | currency ?? "---", 27 | style: context.textTheme.bodyLarge?.copyWith( 28 | fontFeatures: [const FontFeature.tabularFigures()], 29 | ), 30 | maxLines: 1, 31 | overflow: TextOverflow.ellipsis, 32 | ), 33 | const SizedBox(width: 8.0), 34 | Icon(Symbols.arrow_drop_down_rounded), 35 | ], 36 | ), 37 | onTap: onTap, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/widgets/import_wizard/import_item_list_tile.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/widgets/general/flow_icon.dart"; 3 | import "package:flow/widgets/general/surface.dart"; 4 | import "package:flutter/widgets.dart"; 5 | 6 | class ImportItemListTile extends StatelessWidget { 7 | final FlowIconData icon; 8 | final Widget label; 9 | 10 | const ImportItemListTile({ 11 | super.key, 12 | required this.icon, 13 | required this.label, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Surface( 19 | builder: (context) => Padding( 20 | padding: const EdgeInsets.all(16.0), 21 | child: Row( 22 | mainAxisSize: MainAxisSize.max, 23 | children: [ 24 | FlowIcon(icon, size: 24.0), 25 | const SizedBox(width: 8.0), 26 | Flexible(child: label), 27 | const SizedBox(width: 8.0), 28 | ], 29 | ), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/widgets/internal_notifications/internal_notification_section.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/internal_nofications/internal_notification.dart"; 2 | import "package:flow/widgets/internal_notifications/auto_backup_reminder.dart"; 3 | import "package:flow/widgets/internal_notifications/rate_app_notification.dart"; 4 | import "package:flow/widgets/internal_notifications/star_on_github_notification.dart"; 5 | import "package:flutter/material.dart"; 6 | 7 | class InternalNotificationSection extends StatelessWidget { 8 | final InternalNotification notification; 9 | final VoidCallback? onDismiss; 10 | 11 | const InternalNotificationSection({ 12 | super.key, 13 | required this.notification, 14 | this.onDismiss, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) => switch (notification) { 19 | AutoBackupReminder notification => AutoBackupReminderNotification( 20 | notification: notification, 21 | onDismiss: onDismiss, 22 | ), 23 | RateApp notification => RateAppNotification( 24 | notification: notification, 25 | onDismiss: onDismiss, 26 | ), 27 | StarOnGitHub notification => StarOnGithubNotification( 28 | notification: notification, 29 | onDismiss: onDismiss, 30 | ), 31 | _ => SizedBox.shrink(), 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /lib/widgets/internal_notifications/star_on_github_notification.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/constants.dart"; 2 | import "package:flow/data/internal_nofications/internal_notification.dart"; 3 | import "package:flow/l10n/extensions.dart"; 4 | import "package:flow/utils/utils.dart"; 5 | import "package:flow/widgets/internal_notifications/internal_notification_list_tile.dart"; 6 | import "package:flutter/material.dart"; 7 | import "package:material_symbols_icons/material_symbols_icons.dart"; 8 | 9 | class StarOnGithubNotification extends StatelessWidget { 10 | final StarOnGitHub notification; 11 | final VoidCallback? onDismiss; 12 | 13 | const StarOnGithubNotification({ 14 | super.key, 15 | required this.notification, 16 | this.onDismiss, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return InternalNotificationListTile( 22 | onDismiss: onDismiss, 23 | icon: notification.icon, 24 | title: "tabs.home.reminders.starOnGitHub".t(context), 25 | subtitle: "⭐⭐⭐⭐⭐", 26 | action: TextButton.icon( 27 | onPressed: () { 28 | if (onDismiss != null) { 29 | onDismiss!(); 30 | } 31 | openUrl(flowGitHubRepoLink); 32 | }, 33 | label: Text("GitHub"), 34 | icon: Icon(Symbols.open_in_new_rounded), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/widgets/no_result.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | import "package:flow/theme/theme.dart"; 3 | import "package:flutter/material.dart"; 4 | 5 | class NoResult extends StatelessWidget { 6 | const NoResult({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Padding( 11 | padding: const EdgeInsets.all(24.0), 12 | child: Center( 13 | child: Column( 14 | mainAxisSize: MainAxisSize.min, 15 | children: [ 16 | Text( 17 | "transactions.query.noResult".t(context), 18 | textAlign: TextAlign.center, 19 | style: context.textTheme.headlineSmall, 20 | ), 21 | const SizedBox(height: 8.0), 22 | Text( 23 | "transactions.query.noResult.description".t(context), 24 | textAlign: TextAlign.center, 25 | ), 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/widgets/reports/category_report_view.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/reports/category_flow_report.dart"; 2 | import "package:flutter/material.dart"; 3 | 4 | class CategoryReportView extends StatelessWidget { 5 | final CategoryFlowReport report; 6 | 7 | const CategoryReportView({super.key, required this.report}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return const Placeholder(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/widgets/setup/categories/category_preset_card.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/category.dart"; 2 | import "package:flow/utils/optional.dart"; 3 | import "package:flow/widgets/category_card.dart"; 4 | import "package:flutter/material.dart"; 5 | import "package:material_symbols_icons/symbols.dart"; 6 | 7 | class CategoryPresetCard extends StatelessWidget { 8 | final Function(bool) onSelect; 9 | final bool selected; 10 | final bool preexisting; 11 | 12 | final Category category; 13 | 14 | const CategoryPresetCard({ 15 | super.key, 16 | required this.onSelect, 17 | required this.selected, 18 | required this.category, 19 | required this.preexisting, 20 | }); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Opacity( 25 | opacity: selected ? 1.0 : 0.46, 26 | child: CategoryCard( 27 | category: category, 28 | onTapOverride: Optional(() => onSelect(!selected)), 29 | showAmount: false, 30 | trailing: preexisting 31 | ? null 32 | : Icon(selected ? Symbols.remove_rounded : Symbols.add_rounded), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/widgets/setup/offline_slide.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/flow_icon.dart"; 2 | import "package:flow/l10n/extensions.dart"; 3 | import "package:flow/theme/theme.dart"; 4 | import "package:flow/widgets/general/flow_icon.dart"; 5 | import "package:flutter/material.dart"; 6 | import "package:material_symbols_icons/symbols.dart"; 7 | 8 | class OfflineSlide extends StatelessWidget { 9 | const OfflineSlide({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Padding( 14 | padding: const EdgeInsets.all(16.0), 15 | child: Column( 16 | crossAxisAlignment: CrossAxisAlignment.start, 17 | children: [ 18 | const Spacer(), 19 | Center( 20 | child: FlowIcon( 21 | FlowIconData.icon(Symbols.security_rounded), 22 | size: 160.0, 23 | plated: true, 24 | ), 25 | ), 26 | const Spacer(), 27 | Text( 28 | "setup.slides.offline".t(context), 29 | style: context.textTheme.displayMedium?.copyWith( 30 | color: context.colorScheme.primary, 31 | ), 32 | ), 33 | const SizedBox(height: 8.0), 34 | Text( 35 | "setup.slides.offline.description".t(context), 36 | style: context.textTheme.bodyLarge, 37 | ), 38 | const SizedBox(height: 16.0), 39 | ], 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/widgets/setup/welcome_slide.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/l10n/extensions.dart"; 2 | import "package:flow/theme/theme.dart"; 3 | import "package:flutter/material.dart"; 4 | 5 | class WelcomeSlide extends StatelessWidget { 6 | const WelcomeSlide({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Padding( 11 | padding: const EdgeInsets.all(24.0), 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.start, 14 | children: [ 15 | const Spacer(), 16 | Padding( 17 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 18 | child: ClipRRect( 19 | borderRadius: BorderRadius.circular(24.0), 20 | child: Image.asset("assets/images/flow.png"), 21 | ), 22 | ), 23 | const Spacer(), 24 | Text( 25 | "appName".t(context), 26 | style: context.textTheme.displayMedium?.copyWith( 27 | color: context.colorScheme.primary, 28 | ), 29 | ), 30 | const SizedBox(height: 8.0), 31 | Text("appShortDesc".t(context), style: context.textTheme.bodyLarge), 32 | const SizedBox(height: 16.0), 33 | ], 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/widgets/sheets/recurrence/select_day_sheet.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | 3 | class SelectDaySheet extends StatelessWidget { 4 | const SelectDaySheet({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Placeholder(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/widgets/sheets/select_account_type_sheet.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/entity/account.dart"; 2 | import "package:flow/l10n/extensions.dart"; 3 | import "package:flow/l10n/named_enum.dart"; 4 | import "package:flow/widgets/general/directional_chevron.dart"; 5 | import "package:flow/widgets/general/modal_sheet.dart"; 6 | import "package:flutter/material.dart"; 7 | import "package:go_router/go_router.dart"; 8 | 9 | class SelectAccountTypeSheet extends StatelessWidget { 10 | final AccountType? currentlySelected; 11 | 12 | const SelectAccountTypeSheet({super.key, this.currentlySelected}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return ModalSheet.scrollable( 17 | title: Text("account.type".t(context)), 18 | child: Column( 19 | mainAxisSize: MainAxisSize.min, 20 | children: AccountType.values 21 | .where((value) => value != AccountType.creditLine) 22 | .map( 23 | (value) => ListTile( 24 | title: Text(value.localizedNameContext(context)), 25 | selected: currentlySelected == value, 26 | trailing: const DirectionalChevron(), 27 | onTap: () => context.pop(value), 28 | ), 29 | ) 30 | .toList(), 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/widgets/transaction_filter_head.dart: -------------------------------------------------------------------------------- 1 | import "package:flow/data/transaction_filter.dart"; 2 | import "package:flow/theme/theme.dart"; 3 | import "package:flutter/material.dart"; 4 | 5 | /// Renders a row of [TransactionFilterChip]s. 6 | class TransactionFilterHead extends StatelessWidget { 7 | final TransactionFilter value; 8 | 9 | /// Usually List of [TransactionFilterChip]s 10 | final List filterChips; 11 | 12 | final EdgeInsets? padding; 13 | 14 | const TransactionFilterHead({ 15 | super.key, 16 | required this.filterChips, 17 | this.value = TransactionFilter.empty, 18 | this.padding, 19 | }); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | final List children = []; 24 | 25 | for (final chip in filterChips) { 26 | children.add(chip); 27 | children.add(const SizedBox(width: 12.0)); 28 | } 29 | 30 | if (children.isNotEmpty && children.last is SizedBox) { 31 | children.removeLast(); 32 | } 33 | 34 | return Container( 35 | height: 48.0, 36 | width: double.infinity, 37 | color: context.colorScheme.surface, 38 | child: SingleChildScrollView( 39 | padding: padding, 40 | scrollDirection: Axis.horizontal, 41 | child: Row(children: children), 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/widgets/utils/should_execute_scheduled_task.dart: -------------------------------------------------------------------------------- 1 | /// Returns whether this scheduled execution should run "now". 2 | /// 3 | /// Use [anchor] to specify a reference point ("now" point) for the comparison. 4 | /// 5 | /// Always returns `true` if [lastExecution] is `null`. 6 | /// 7 | /// Returns `false` if [interval] is negative. 8 | bool shouldExecuteScheduledTask( 9 | Duration interval, 10 | DateTime? lastExecution, { 11 | DateTime? anchor, 12 | }) { 13 | if (lastExecution == null) { 14 | return true; 15 | } 16 | 17 | if (interval.isNegative) { 18 | return false; 19 | } 20 | 21 | final DateTime now = anchor ?? DateTime.now(); 22 | 23 | if (lastExecution.add(interval).isAfter(now)) { 24 | return false; 25 | } 26 | 27 | return true; 28 | } 29 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | desktop_drop 7 | file_saver 8 | file_selector_linux 9 | flutter_timezone 10 | objectbox_flutter_libs 11 | screen_retriever_linux 12 | url_launcher_linux 13 | window_manager 14 | ) 15 | 16 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 17 | ) 18 | 19 | set(PLUGIN_BUNDLED_LIBRARIES) 20 | 21 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 23 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 26 | endforeach(plugin) 27 | 28 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 29 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 30 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 31 | endforeach(ffi_plugin) 32 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /logo@16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/logo@16.png -------------------------------------------------------------------------------- /logo@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/logo@32.png -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = Flow 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = mn.flow.flow 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 Batmend Ganbaatar and Authors of Flow All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | 4 | PRODUCT_NAME = Flow (dev) -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.files.user-selected.read-only 10 | 11 | com.apple.security.network.client 12 | 13 | com.apple.security.network.server 14 | 15 | com.apple.security.application-groups 16 | 17 | NJH37247C9.flow 18 | 19 | com.apple.security.files.downloads.read-write 20 | 21 | 22 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | com.apple.security.files.downloads.read-write 32 | 33 | NSLocationWhenInUseUsageDescription 34 | Location is used if you choose to auto-attach your current location to your transactions. 35 | 36 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | com.apple.security.application-groups 14 | 15 | NJH37247C9.flow 16 | 17 | com.apple.security.files.downloads.read-write 18 | 19 | 20 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /reformat_l10n_files.sh: -------------------------------------------------------------------------------- 1 | dart ./scripts/reformat_l10n_files.dart -------------------------------------------------------------------------------- /regenerate_code.sh: -------------------------------------------------------------------------------- 1 | dart run build_runner build --delete-conflicting-outputs && dart format lib 2 | -------------------------------------------------------------------------------- /scripts/reformat_l10n_files.dart: -------------------------------------------------------------------------------- 1 | import "dart:convert"; 2 | import "dart:io"; 3 | 4 | void reformat(File file) { 5 | final String content = file.readAsStringSync(); 6 | final Map jsonMap = jsonDecode(content); 7 | final List keys = jsonMap.keys.toList(); 8 | final List sortedKeys = List.from(keys)..sort(); 9 | final Map sortedJsonMap = {}; 10 | for (String key in sortedKeys) { 11 | sortedJsonMap[key] = jsonMap[key]; 12 | } 13 | 14 | file.writeAsStringSync(JsonEncoder.withIndent(" ").convert(sortedJsonMap)); 15 | } 16 | 17 | void main() { 18 | final Directory directory = Directory("assets/l10n"); 19 | 20 | if (!directory.existsSync()) { 21 | throw Exception("Directory does not exist"); 22 | } 23 | 24 | directory 25 | .listSync() 26 | .where((entry) => entry is File && entry.path.endsWith(".json")) 27 | .cast() 28 | .forEach(reformat); 29 | } 30 | -------------------------------------------------------------------------------- /test/backup/v1_export.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/backup/v1_import.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/test/backup/v1_import.dart -------------------------------------------------------------------------------- /test/import/csv_data_parser_test.dart: -------------------------------------------------------------------------------- 1 | import "dart:io"; 2 | 3 | import "package:flow/sync/model/csv/parsed_data.dart"; 4 | import "package:flow/sync/model/csv/parsers.dart"; 5 | import "package:flutter_test/flutter_test.dart"; 6 | 7 | void main() { 8 | test("A valid csv", () async { 9 | final valid = await CSVParsedData.fromFile( 10 | File("test/import/valid-1.csv"), 11 | ).then((v) => v as CSVParsedData?).catchError((e) => null); 12 | 13 | expect(valid, isNotNull); 14 | expect(valid?.accountNames.length, 3); 15 | expect(valid?.categoryNames.nonNulls.length, 24); 16 | expect(valid?.transactions.length, 81); 17 | }); 18 | test("An invalid csv", () async { 19 | final invalid = await CSVParsedData.fromFile( 20 | File("test/import/invalid-1.csv"), 21 | ).then((e) => e as dynamic).catchError((e) => e); 22 | 23 | expect(invalid, CSVCellParserError.invalidDate); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /test/l10n/json_integrity_test.dart: -------------------------------------------------------------------------------- 1 | import "dart:convert"; 2 | import "dart:io"; 3 | 4 | import "package:test/test.dart"; 5 | import "package:path/path.dart"; 6 | 7 | List getKeys(File file) { 8 | final String content = file.readAsStringSync(); 9 | final Map jsonMap = jsonDecode(content); 10 | return jsonMap.keys.toList(); 11 | } 12 | 13 | void main() { 14 | final Directory directory = Directory("assets/l10n"); 15 | 16 | final File baseFile = File("assets/l10n/en.json"); 17 | 18 | test("Directory exists", () { 19 | expect(directory.existsSync(), true); 20 | }); 21 | 22 | test("Base file exists", () { 23 | expect(baseFile.existsSync(), true); 24 | }); 25 | 26 | final List keys = getKeys(baseFile); 27 | 28 | test("No duplicate keys in base file", () { 29 | final Set uniqueKeys = keys.toSet(); 30 | expect(uniqueKeys.length, keys.length); 31 | }); 32 | 33 | for (final entry in directory.listSync()) { 34 | if (entry is! File) continue; 35 | if (!entry.path.endsWith(".json")) continue; 36 | if (entry.path == baseFile.path) continue; 37 | 38 | test("File ${basename(entry.path)} has all keys in same order", () { 39 | final languageKeys = getKeys(entry); 40 | 41 | for (int i = 0; i < keys.length; i++) { 42 | expect(languageKeys[i], keys[i]); 43 | } 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/objectbox_erase.dart: -------------------------------------------------------------------------------- 1 | import "dart:io"; 2 | 3 | import "package:flow/objectbox.dart"; 4 | 5 | Future testCleanupObject({ 6 | required ObjectBox instance, 7 | required String directory, 8 | bool cleanUp = true, 9 | }) async { 10 | instance.store.close(); 11 | 12 | if (cleanUp) { 13 | await Directory(directory).delete(recursive: true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | connectivity_plus 7 | desktop_drop 8 | file_saver 9 | file_selector_windows 10 | flutter_timezone 11 | geolocator_windows 12 | local_auth_windows 13 | objectbox_flutter_libs 14 | screen_retriever_windows 15 | share_plus 16 | url_launcher_windows 17 | window_manager 18 | ) 19 | 20 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 21 | flutter_local_notifications_windows 22 | ) 23 | 24 | set(PLUGIN_BUNDLED_LIBRARIES) 25 | 26 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 27 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 28 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 29 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 30 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 31 | endforeach(plugin) 32 | 33 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 34 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 35 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 36 | endforeach(ffi_plugin) 37 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"Flow", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flow-mn/flow/632f761cf1ad6d1463b296331b5af2679e900f2f/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------