From 58d3e3ea2278bfef558895c71b51504c1a57903b Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Wed, 18 Feb 2026 09:14:53 +0200 Subject: [PATCH 01/20] feat: Integrate Firebase and configure release signing --- android/app/build.gradle.kts | 54 ++++++++++++++++++++++++++---------- android/settings.gradle.kts | 4 +++ pubspec.yaml | 6 ++++ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 4cb152db..f8432080 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,15 +1,34 @@ +import java.util.Properties +import java.io.FileInputStream plugins { id("com.android.application") + // START: FlutterFire Configuration + id("com.google.gms.google-services") + id("com.google.firebase.crashlytics") + // END: FlutterFire Configuration id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") } +val keystoreProperties = Properties() +val keystorePropertiesFile = rootProject.file("key.properties") +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) +} + android { namespace = "com.pennypilot.moneyplus" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion + defaultConfig { + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -19,26 +38,33 @@ android { jvmTarget = JavaVersion.VERSION_17.toString() } - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.pennypilot.moneyplus" - // You can update the following values to match your application needs. - // For more information, see: https://flutter.dev/to/review-gradle-config. - minSdk = flutter.minSdkVersion - targetSdk = flutter.targetSdkVersion - versionCode = flutter.versionCode - versionName = flutter.versionName + signingConfigs { + create("release") { + keyAlias = keystoreProperties.getProperty("keyAlias") + keyPassword = keystoreProperties.getProperty("keyPassword") + storeFile = file(keystoreProperties.getProperty("storeFile")) + storePassword = keystoreProperties.getProperty("storePassword") + } } buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.getByName("debug") + + getByName("release") { + signingConfig = signingConfigs.getByName("release") + isMinifyEnabled = true + isShrinkResources = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + ndk { + abiFilters.clear() + abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a")) + } + } } } - flutter { source = "../.." } diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index ca7fe065..d6b1b1bf 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -20,6 +20,10 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "8.11.1" apply false + // START: FlutterFire Configuration + id("com.google.gms.google-services") version("4.3.15") apply false + id("com.google.firebase.crashlytics") version("2.8.1") apply false + // END: FlutterFire Configuration id("org.jetbrains.kotlin.android") version "2.2.20" apply false } diff --git a/pubspec.yaml b/pubspec.yaml index 2daf09a2..2e466a64 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,6 +36,10 @@ dependencies: # --- Utils & Internationalization --- intl: any + firebase_core: ^4.4.0 + firebase_crashlytics: ^5.0.7 + firebase_remote_config: ^6.1.4 + firebase_analytics: ^12.1.1 dev_dependencies: flutter_test: @@ -45,6 +49,8 @@ dev_dependencies: go_router_builder: ^4.0.1 build_runner: ^2.6.0 build_verify: any + integration_test: + sdk: flutter flutter: generate: true From b35c9927d837cc40a2293af5e3fe72dd151d6e83 Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Wed, 18 Feb 2026 23:22:51 +0200 Subject: [PATCH 02/20] Refactor: Separate dependency injection setup into multiple files --- lib/core/di/cubit_di.dart | 57 +++++++++++++++++++++ lib/core/di/injection.dart | 91 +++------------------------------- lib/core/di/repository_di.dart | 36 ++++++++++++++ lib/core/di/service_di.dart | 14 ++++++ 4 files changed, 114 insertions(+), 84 deletions(-) create mode 100644 lib/core/di/cubit_di.dart create mode 100644 lib/core/di/repository_di.dart create mode 100644 lib/core/di/service_di.dart diff --git a/lib/core/di/cubit_di.dart b/lib/core/di/cubit_di.dart new file mode 100644 index 00000000..178e7dcd --- /dev/null +++ b/lib/core/di/cubit_di.dart @@ -0,0 +1,57 @@ +import 'package:get_it/get_it.dart'; +import 'package:moneyplus/domain/repository/account_repository.dart'; +import 'package:moneyplus/domain/repository/authentication_repository.dart'; +import 'package:moneyplus/domain/repository/statistics_repository.dart'; +import 'package:moneyplus/domain/repository/transaction_repository.dart'; +import 'package:moneyplus/domain/repository/user_money_repository.dart'; +import 'package:moneyplus/domain/validator/authentication_validator.dart'; +import 'package:moneyplus/presentation/account_setup/cubit/account_setup_cubit.dart'; +import 'package:moneyplus/presentation/home/cubit/home_cubit.dart'; +import 'package:moneyplus/presentation/income/cubit/add_income_cubit.dart'; +import 'package:moneyplus/presentation/login/cubit/login_cubit.dart'; +import 'package:moneyplus/presentation/statistics/cubit/statistics_cubit.dart'; +import 'package:moneyplus/presentation/transactions/cubit/transaction_cubit.dart'; +import 'package:moneyplus/presentation/trasnaction_details/trasnaction_details_cubit.dart'; + +import 'injection.dart'; + + +void initCubitDI() { + getIt.registerLazySingleton( + () => AuthenticationValidator(), + ); + + getIt.registerFactory( + () => HomeCubit(userMoneyRepository: getIt()), + ); + + getIt.registerFactory( + () => LoginCubit( + authRepository: getIt(), + validator: getIt(), + ), + ); + + getIt.registerLazySingleton( + () => AccountSetupCubit(getIt()), + ); + + getIt.registerFactory( + () => AddIncomeCubit(repository: getIt()), + ); + + getIt.registerFactory( + () => + TransactionCubit(transactionRepository: getIt()), + ); + + getIt.registerFactory( + () => StatisticsCubit(repository: getIt()), + ); + + getIt.registerFactory( + () => TransactionDetailsCubit( + transactionRepository: getIt(), + ), + ); +} diff --git a/lib/core/di/injection.dart b/lib/core/di/injection.dart index b7f9e12e..66c8f9d8 100644 --- a/lib/core/di/injection.dart +++ b/lib/core/di/injection.dart @@ -1,90 +1,13 @@ import 'package:get_it/get_it.dart'; -import 'package:moneyplus/domain/repository/transaction_repository.dart'; -import 'package:moneyplus/presentation/account_setup/cubit/account_setup_cubit.dart'; -import 'package:moneyplus/presentation/transactions/cubit/transaction_cubit.dart'; -import '../../data/repository/account_repository.dart'; -import '../../data/repository/authentication_repository.dart'; -import '../../data/repository/fake_statistics_repository.dart'; -import '../../data/repository/statistics_repository_impl.dart'; -import '../../data/repository/transaction_repository_stub.dart'; -import '../../data/repository/user_money_repository.dart'; -import '../../data/service/app_secrets_provider.dart'; -import '../../data/service/supabase_service.dart'; -import '../../domain/repository/account_repository.dart'; -import '../../domain/repository/authentication_repository.dart'; -import '../../domain/repository/statistics_repository.dart'; -import '../../domain/repository/user_money_repository.dart'; -import '../../domain/validator/authentication_validator.dart'; -import '../../presentation/home/cubit/home_cubit.dart'; -import '../../presentation/income/cubit/add_income_cubit.dart'; -import '../../presentation/login/cubit/login_cubit.dart'; -import '../../presentation/statistics/cubit/statistics_cubit.dart'; -import '../../presentation/trasnaction_details/trasnaction_details_cubit.dart'; +import 'package:moneyplus/core/di/cubit_di.dart'; +import 'package:moneyplus/core/di/repository_di.dart'; +import 'package:moneyplus/core/di/service_di.dart'; final getIt = GetIt.instance; void initDI() { - getIt.registerLazySingleton(() => AppSecretsProvider()); - getIt.registerLazySingleton( - () => SupabaseService(appSecretsProvider: getIt()), - ); - - getIt.registerLazySingleton( - () => AuthenticationRepositoryImpl( - supabaseService: getIt(), - appSecrets: getIt(), - ), - ); - getIt.registerLazySingleton( - () => UserRepositoryImpl(service: getIt()), - ); - - getIt.registerFactory( - () => HomeCubit(userMoneyRepository: getIt()), - ); - getIt.registerLazySingleton( - () => AuthenticationValidator(), - ); - - getIt.registerFactory( - () => LoginCubit( - authRepository: getIt(), - validator: getIt(), - ), - ); - - getIt.registerLazySingleton( - () => AccountRepositoryImpl(supabaseService: getIt()), - ); - - getIt.registerLazySingleton( - () => TransactionRepositoryStub(getIt()), - ); - - getIt.registerLazySingleton( - () => AccountSetupCubit(getIt()), - ); - - getIt.registerFactory( - () => AddIncomeCubit(repository: getIt()), - ); - - getIt.registerFactory( - () => - TransactionCubit(transactionRepository: getIt()), - ); - - getIt.registerLazySingleton( - () => StatisticsRepositoryImpl(supabaseService: getIt()), - ); - - getIt.registerFactory( - () => StatisticsCubit(repository: getIt()), - ); - - getIt.registerFactory( - () => TransactionDetailsCubit( - transactionRepository: getIt(), - ), - ); + // Initialize services, repositories and cubits from other files + initServiceDI(); + initRepositoryDI(); + initCubitDI(); } diff --git a/lib/core/di/repository_di.dart b/lib/core/di/repository_di.dart new file mode 100644 index 00000000..652f9782 --- /dev/null +++ b/lib/core/di/repository_di.dart @@ -0,0 +1,36 @@ +import 'package:get_it/get_it.dart'; + +import '../../data/repository/account_repository.dart'; +import '../../data/repository/authentication_repository.dart'; +import '../../data/repository/statistics_repository_impl.dart'; +import '../../data/repository/transaction_repository_stub.dart'; +import '../../data/repository/user_money_repository.dart'; +import '../../data/service/supabase_service.dart'; +import '../../domain/repository/account_repository.dart'; +import '../../domain/repository/authentication_repository.dart'; +import '../../domain/repository/statistics_repository.dart'; +import '../../domain/repository/transaction_repository.dart'; +import '../../domain/repository/user_money_repository.dart'; + +import 'injection.dart'; + +void initRepositoryDI() { + getIt.registerLazySingleton( + () => AuthenticationRepositoryImpl( + supabaseService: getIt(), + appSecrets: getIt(), + ), + ); + getIt.registerLazySingleton( + () => UserRepositoryImpl(service: getIt()), + ); + getIt.registerLazySingleton( + () => AccountRepositoryImpl(supabaseService: getIt()), + ); + getIt.registerLazySingleton( + () => TransactionRepositoryStub(getIt()), + ); + getIt.registerLazySingleton( + () => StatisticsRepositoryImpl(supabaseService: getIt()), + ); +} diff --git a/lib/core/di/service_di.dart b/lib/core/di/service_di.dart new file mode 100644 index 00000000..0174e0f3 --- /dev/null +++ b/lib/core/di/service_di.dart @@ -0,0 +1,14 @@ + + +import '../../data/service/app_secrets_provider.dart'; +import '../../data/service/supabase_service.dart'; + +import 'injection.dart'; + +void initServiceDI() { + getIt.registerLazySingleton(() => AppSecretsProvider()); + getIt.registerLazySingleton( + () => SupabaseService(appSecretsProvider: getIt()), + ); + +} From eb1893291f522617ad054d3c4d2c0f0a0c04675a Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Wed, 18 Feb 2026 23:23:33 +0200 Subject: [PATCH 03/20] feat: Add Google client ID constants --- lib/core/constants/app_constants.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/constants/app_constants.dart b/lib/core/constants/app_constants.dart index f8606b98..8e3ab8da 100644 --- a/lib/core/constants/app_constants.dart +++ b/lib/core/constants/app_constants.dart @@ -8,4 +8,7 @@ class AppConstants { // Deep link paths static const String resetPasswordRedirect = "com.pennypilot.moneyplus://reset-password"; + + static const String googleWebClientId = "GOOGLE_WEB_CLIENT_ID"; + static const String googleIosClientId = "GOOGLE_IOS_CLIENT_ID"; } From d19be78438937f79f720c44cf49d1721382dadd8 Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 01:05:27 +0200 Subject: [PATCH 04/20] feat: enhance CI/CD workflows for Android and iOS --- .github/workflows/android.yml | 109 ++++++++++++++++++++++++++++++++++ .github/workflows/ios.yml | 32 ++++++++++ 2 files changed, 141 insertions(+) create mode 100644 .github/workflows/android.yml create mode 100644 .github/workflows/ios.yml diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 00000000..4dc8d42f --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,109 @@ +name: Flutter CI + +on: + push: + branches: [ "development" , "main" ] + pull_request: + branches: [ "development" , "main" ] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 45 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + cache: true + + - name: Cache Gradle packages + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache pub dependencies + uses: actions/cache@v3 + with: + path: | + ${{ env.PUB_CACHE }} + ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} + restore-keys: | + ${{ runner.os }}-pub- + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install Firebase CLI + run: npm install -g firebase-tools + + - name: Install FlutterFire CLI + run: dart pub global activate flutterfire_cli + - name: Install dependencies + run: flutter pub get + + - name: Generate local code + run: flutter pub run build_runner build --delete-conflicting-outputs + + - name: Generate Firebase Options + env: + FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }} + FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + run: flutterfire configure --project=$FIREBASE_PROJECT_ID -y --platforms=android --out=lib/firebase_options.dart + + - name: Configure Gradle memory + run: | + mkdir -p ~/.gradle + cat > ~/.gradle/gradle.properties << EOF + org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError + org.gradle.daemon=false + org.gradle.parallel=true + org.gradle.caching=true + android.useAndroidX=true + android.enableJetifier=true + EOF + + - name: Decode keystore + env: + ANDROID_KEYSTORE_BASE64: ${{ secrets.SIGNING_KEY_BASE64 }} + run: echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > android/app/upload-keystore.jks + + - name: Create key.properties + env: + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_ALIAS_PASSWORD: ${{ secrets.ANDROID_KEY_ALIAS_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + run: | + cat < android/key.properties + storePassword=$ANDROID_KEYSTORE_PASSWORD + keyPassword=$ANDROID_KEY_ALIAS_PASSWORD + keyAlias=$ANDROID_KEY_ALIAS + storeFile=upload-keystore.jks + EOF + + - name: Build release APK + timeout-minutes: 30 + run: flutter build apk --release --obfuscate --split-debug-info=build/app/outputs/symbols + + - name: Distribute to Firebase + env: + FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }} + FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + run: | + firebase appdistribution:distribute build/app/outputs/flutter-apk/app-release.apk \ + --app "$FIREBASE_APP_ID" \ + --groups "closed-beta-testers" \ + --release-notes "Build from ${{ github.ref_name }} - Commit: ${{ github.sha }}" \ No newline at end of file diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml new file mode 100644 index 00000000..b13dfe28 --- /dev/null +++ b/.github/workflows/ios.yml @@ -0,0 +1,32 @@ +name: Flutter CI iOS + +on: + push: + branches: [ "development", "main" ] + pull_request: + branches: [ "development", "main" ] + workflow_dispatch: + +jobs: + build-ios: + runs-on: macos-latest + timeout-minutes: 60 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + cache: true + + - name: Install dependencies + run: flutter pub get + + - name: Generate local code + run: flutter pub run build_runner build --delete-conflicting-outputs + + - name: Build iOS (simulator) + run: flutter build ios --simulator \ No newline at end of file From bbca8527f651e1fe8a56303a805c732184a7040e Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 01:06:22 +0200 Subject: [PATCH 05/20] feat: Integrate Firebase Remote Config for secrets management --- lib/core/security/app_secrets.dart | 29 +++++++++++++++++ lib/core/service/firebase_service.dart | 38 ++++++++++++++++++++++ lib/data/service/app_secrets_provider.dart | 15 --------- 3 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 lib/core/security/app_secrets.dart create mode 100644 lib/core/service/firebase_service.dart delete mode 100644 lib/data/service/app_secrets_provider.dart diff --git a/lib/core/security/app_secrets.dart b/lib/core/security/app_secrets.dart new file mode 100644 index 00000000..d6376a11 --- /dev/null +++ b/lib/core/security/app_secrets.dart @@ -0,0 +1,29 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; + +import '../constants/app_constants.dart'; + +class AppSecrets { + + final FirebaseRemoteConfig firebaseRemoteConfig; + AppSecrets({required this.firebaseRemoteConfig}); + + Future fetchRemoteConfig() async { + await firebaseRemoteConfig.fetchAndActivate(); + } + + String getRemoteConfigSupaBaseUrl() { + return firebaseRemoteConfig.getString(AppConstants.supabaseUrl); + } + + String getRemoteConfigSupaBaseApiKey() { + return firebaseRemoteConfig.getString(AppConstants.supabaseApiKey); + } + + String getRemoteConfigGoogeWebClientId() { + return firebaseRemoteConfig.getString(AppConstants.googleWebClientId); + } + + String getRemoteConfigGoogeIosClientId() { + return firebaseRemoteConfig.getString(AppConstants.googleIosClientId); + } +} \ No newline at end of file diff --git a/lib/core/service/firebase_service.dart b/lib/core/service/firebase_service.dart new file mode 100644 index 00000000..c7fc15b7 --- /dev/null +++ b/lib/core/service/firebase_service.dart @@ -0,0 +1,38 @@ + + +import 'dart:ui'; + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:flutter/cupertino.dart'; + +import '../../firebase_options.dart'; + +class FirebaseService { + late final FirebaseRemoteConfig remoteConfig; + + Future init() async { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + FlutterError.onError = (errorDetails) { + FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails); + }; + PlatformDispatcher.instance.onError = (error, stack) { + FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); + return true; + }; + + remoteConfig = FirebaseRemoteConfig.instance; + await remoteConfig.setConfigSettings(RemoteConfigSettings( + fetchTimeout: const Duration(seconds: 10), + minimumFetchInterval: const Duration(hours: 1), + )); + await remoteConfig.fetchAndActivate(); + } + + Future getRemoteConfig() async { + return remoteConfig; + } +} diff --git a/lib/data/service/app_secrets_provider.dart b/lib/data/service/app_secrets_provider.dart deleted file mode 100644 index 05399656..00000000 --- a/lib/data/service/app_secrets_provider.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter_dotenv/flutter_dotenv.dart'; - -import '../../core/constants/app_constants.dart'; - - -class AppSecretsProvider { - DotEnv? _dotenv; - - Future getEnvVariables() async { - if (_dotenv != null) return _dotenv!; - _dotenv = DotEnv(); - await _dotenv!.load(fileName: AppConstants.secretsEnvFile); - return _dotenv!; - } -} From 43d2e05623d71ad11b6bb8f908f2b32eb6338c1e Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 01:09:01 +0200 Subject: [PATCH 06/20] Refactor: add firebase config di and use the new secrets class in repositories --- lib/core/di/injection.dart | 2 +- lib/core/di/repository_di.dart | 2 +- lib/core/di/service_di.dart | 29 +++++++++++++------ .../service/supabase_service.dart | 16 +++++----- lib/data/repository/account_repository.dart | 2 +- .../repository/authentication_repository.dart | 16 ++++------ .../statistics_repository_impl.dart | 2 +- .../transaction_repository_stub.dart | 4 +-- .../repository/user_money_repository.dart | 2 +- lib/main.dart | 3 +- 10 files changed, 41 insertions(+), 37 deletions(-) rename lib/{data => core}/service/supabase_service.dart (51%) diff --git a/lib/core/di/injection.dart b/lib/core/di/injection.dart index 66c8f9d8..a276fe85 100644 --- a/lib/core/di/injection.dart +++ b/lib/core/di/injection.dart @@ -5,7 +5,7 @@ import 'package:moneyplus/core/di/service_di.dart'; final getIt = GetIt.instance; -void initDI() { +void initDI() { // Initialize services, repositories and cubits from other files initServiceDI(); initRepositoryDI(); diff --git a/lib/core/di/repository_di.dart b/lib/core/di/repository_di.dart index 652f9782..58ebd281 100644 --- a/lib/core/di/repository_di.dart +++ b/lib/core/di/repository_di.dart @@ -5,7 +5,7 @@ import '../../data/repository/authentication_repository.dart'; import '../../data/repository/statistics_repository_impl.dart'; import '../../data/repository/transaction_repository_stub.dart'; import '../../data/repository/user_money_repository.dart'; -import '../../data/service/supabase_service.dart'; +import '../service/supabase_service.dart'; import '../../domain/repository/account_repository.dart'; import '../../domain/repository/authentication_repository.dart'; import '../../domain/repository/statistics_repository.dart'; diff --git a/lib/core/di/service_di.dart b/lib/core/di/service_di.dart index 0174e0f3..8622e523 100644 --- a/lib/core/di/service_di.dart +++ b/lib/core/di/service_di.dart @@ -1,14 +1,25 @@ +import 'package:moneyplus/core/security/app_secrets.dart'; - -import '../../data/service/app_secrets_provider.dart'; -import '../../data/service/supabase_service.dart'; - +import '../service/firebase_service.dart'; +import '../service/supabase_service.dart'; import 'injection.dart'; -void initServiceDI() { - getIt.registerLazySingleton(() => AppSecretsProvider()); - getIt.registerLazySingleton( - () => SupabaseService(appSecretsProvider: getIt()), +void initServiceDI() { + getIt.registerSingletonAsync(() async { + final service = FirebaseService(); + await service.init(); + return service; + }); + + getIt.registerSingletonAsync( + () async => AppSecrets( + firebaseRemoteConfig: getIt().remoteConfig, + ), + dependsOn: [FirebaseService], ); -} + getIt.registerSingletonAsync( + () async => SupabaseService(appSecrets: getIt()), + dependsOn: [AppSecrets], + ); +} \ No newline at end of file diff --git a/lib/data/service/supabase_service.dart b/lib/core/service/supabase_service.dart similarity index 51% rename from lib/data/service/supabase_service.dart rename to lib/core/service/supabase_service.dart index adc22da6..f6ddb783 100644 --- a/lib/data/service/supabase_service.dart +++ b/lib/core/service/supabase_service.dart @@ -1,29 +1,27 @@ import 'package:flutter/foundation.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; -import '../../core/constants/app_constants.dart'; -import 'app_secrets_provider.dart'; +import '../security/app_secrets.dart'; class SupabaseService { SupabaseClient? _supabaseClient; - final AppSecretsProvider appSecretsProvider; + AppSecrets appSecrets; - SupabaseService({required this.appSecretsProvider}); + SupabaseService({required this.appSecrets}); Future getClient() async { if (_supabaseClient != null) return _supabaseClient!; - final dotEnvInstance = await appSecretsProvider.getEnvVariables(); + final url = appSecrets.getRemoteConfigSupaBaseUrl(); + final anonKey = appSecrets.getRemoteConfigSupaBaseApiKey(); final supabase = await Supabase.initialize( - url: dotEnvInstance.env[AppConstants.supabaseUrl] ?? "", - anonKey: dotEnvInstance.env[AppConstants.supabaseApiKey] ?? "", + url: url, + anonKey: anonKey, debug: kDebugMode, ); _supabaseClient = supabase.client; - - return _supabaseClient!; } } diff --git a/lib/data/repository/account_repository.dart b/lib/data/repository/account_repository.dart index f99ffb1f..b936dcf5 100644 --- a/lib/data/repository/account_repository.dart +++ b/lib/data/repository/account_repository.dart @@ -1,7 +1,7 @@ import 'package:moneyplus/domain/entity/currency.dart'; import '../../domain/repository/account_repository.dart'; -import '../service/supabase_service.dart'; +import '../../core/service/supabase_service.dart'; class AccountRepositoryImpl extends AccountRepository { final SupabaseService supabaseService; diff --git a/lib/data/repository/authentication_repository.dart b/lib/data/repository/authentication_repository.dart index 580675da..1d7f69b2 100644 --- a/lib/data/repository/authentication_repository.dart +++ b/lib/data/repository/authentication_repository.dart @@ -1,20 +1,20 @@ import 'dart:developer'; import 'package:google_sign_in/google_sign_in.dart'; +import 'package:moneyplus/core/security/app_secrets.dart'; import 'package:supabase_flutter/supabase_flutter.dart' hide User; import '../../core/constants/app_constants.dart'; import '../../core/errors/error_model.dart'; import '../../core/errors/result.dart'; import '../../core/errors/supabase_auth_error.dart'; +import '../../core/service/supabase_service.dart'; import '../../domain/entity/user.dart'; import '../../domain/repository/authentication_repository.dart'; -import '../service/app_secrets_provider.dart'; -import '../service/supabase_service.dart'; class AuthenticationRepositoryImpl implements AuthenticationRepository { final SupabaseService supabaseService; - final AppSecretsProvider appSecrets; + final AppSecrets appSecrets; AuthenticationRepositoryImpl({ required this.supabaseService, @@ -24,14 +24,10 @@ class AuthenticationRepositoryImpl implements AuthenticationRepository { @override Future> signInWithGoogle() async { try { - final env = await appSecrets.getEnvVariables(); + final serverClientId = appSecrets.getRemoteConfigGoogeWebClientId(); + final clientId = appSecrets.getRemoteConfigGoogeIosClientId(); final GoogleSignIn signIn = GoogleSignIn.instance; - ( - signIn.initialize( - serverClientId: env.env[_googleWebClientId] ?? "", - clientId: env.env[_googleIosClientId] ?? "", - ), - ); + (signIn.initialize(serverClientId: serverClientId, clientId: clientId),); final googleAccount = await signIn.authenticate(); final googleAuthorization = await googleAccount.authorizationClient diff --git a/lib/data/repository/statistics_repository_impl.dart b/lib/data/repository/statistics_repository_impl.dart index 70cca751..2ea33b3b 100644 --- a/lib/data/repository/statistics_repository_impl.dart +++ b/lib/data/repository/statistics_repository_impl.dart @@ -1,4 +1,4 @@ -import '../../data/service/supabase_service.dart'; +import '../../core/service/supabase_service.dart'; import '../../domain/entity/monthly_overview.dart'; import '../../domain/repository/statistics_repository.dart'; diff --git a/lib/data/repository/transaction_repository_stub.dart b/lib/data/repository/transaction_repository_stub.dart index 4c13ea71..c19983a8 100644 --- a/lib/data/repository/transaction_repository_stub.dart +++ b/lib/data/repository/transaction_repository_stub.dart @@ -1,13 +1,11 @@ import 'package:moneyplus/core/errors/error_model.dart'; import 'package:moneyplus/core/errors/result.dart'; -import 'package:moneyplus/data/repository/utils/fake_data.dart'; -import 'package:moneyplus/data/service/supabase_service.dart'; +import 'package:moneyplus/core/service/supabase_service.dart'; import 'package:moneyplus/domain/entity/transaction.dart'; import 'package:moneyplus/domain/entity/transaction_category.dart'; import 'package:moneyplus/domain/entity/transaction_type.dart'; import 'package:moneyplus/domain/repository/model/top_spending_category.dart'; import 'package:moneyplus/domain/repository/transaction_repository.dart'; -import 'package:supabase_flutter/supabase_flutter.dart'; class TransactionRepositoryStub implements TransactionRepository { final SupabaseService service; diff --git a/lib/data/repository/user_money_repository.dart b/lib/data/repository/user_money_repository.dart index 89057301..d510a8cf 100644 --- a/lib/data/repository/user_money_repository.dart +++ b/lib/data/repository/user_money_repository.dart @@ -1,4 +1,4 @@ -import 'package:moneyplus/data/service/supabase_service.dart'; +import 'package:moneyplus/core/service/supabase_service.dart'; import 'package:moneyplus/domain/entity/transaction_category.dart'; import 'package:moneyplus/domain/repository/model/top_spending_category.dart'; import 'package:moneyplus/domain/repository/user_money_repository.dart'; diff --git a/lib/main.dart b/lib/main.dart index fc7f6a58..f0393d0c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,7 @@ import 'package:logging/logging.dart'; import 'package:moneyplus/money_app.dart'; import 'core/di/injection.dart'; -void main() { +void main() async { WidgetsFlutterBinding.ensureInitialized(); Logger.root.level = Level.ALL; Logger.root.onRecord.listen((record) { @@ -13,6 +13,7 @@ void main() { } }); initDI(); + await getIt.allReady(); runApp(const MoneyApp()); } From c316e0e43c1567f3de626345d9cb9629cb6370b3 Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 01:09:14 +0200 Subject: [PATCH 07/20] feat: Remove old Flutter CI workflow --- .github/workflows/flutter_build.yml | 59 ----------------------------- 1 file changed, 59 deletions(-) delete mode 100644 .github/workflows/flutter_build.yml diff --git a/.github/workflows/flutter_build.yml b/.github/workflows/flutter_build.yml deleted file mode 100644 index 59f8763f..00000000 --- a/.github/workflows/flutter_build.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Flutter CI - -on: - push: - branches: [dev, main] - pull_request: - branches: [dev, main] -jobs: - build: - name: App Build (Android & iOS) - runs-on: macos-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable - - - name: Cache Gradle packages - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Cache pub dependencies - uses: actions/cache@v3 - with: - path: | - ${{ env.PUB_CACHE }} - ~/.pub-cache - key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} - restore-keys: | - ${{ runner.os }}-pub- - - name: Generate local code - run: flutter pub run build_runner build --delete-conflicting-outputs - - - name: Create secrets.env file - run: | - echo "SUPA_BASE_URL=${{ secrets.SUPA_BASE_URL }}" > secrets.env - echo "SUPA_API_KEY=${{ secrets.SUPA_API_KEY }}" >> secrets.env - - - name: Flutter clean - run: flutter clean - - - name: Flutter pub get - run: flutter pub get - - - name: Build Android - run: flutter build apk --debug - - - name: Build iOS (simulator) - run: flutter build ios --simulator From ed81f283711218ab9b146897738e9b915ae4ee16 Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 01:10:14 +0200 Subject: [PATCH 08/20] refactor optimize unused code --- lib/core/di/repository_di.dart | 5 +---- lib/data/repository/authentication_repository.dart | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/core/di/repository_di.dart b/lib/core/di/repository_di.dart index 58ebd281..c630393b 100644 --- a/lib/core/di/repository_di.dart +++ b/lib/core/di/repository_di.dart @@ -1,17 +1,14 @@ -import 'package:get_it/get_it.dart'; - import '../../data/repository/account_repository.dart'; import '../../data/repository/authentication_repository.dart'; import '../../data/repository/statistics_repository_impl.dart'; import '../../data/repository/transaction_repository_stub.dart'; import '../../data/repository/user_money_repository.dart'; -import '../service/supabase_service.dart'; import '../../domain/repository/account_repository.dart'; import '../../domain/repository/authentication_repository.dart'; import '../../domain/repository/statistics_repository.dart'; import '../../domain/repository/transaction_repository.dart'; import '../../domain/repository/user_money_repository.dart'; - +import '../service/supabase_service.dart'; import 'injection.dart'; void initRepositoryDI() { diff --git a/lib/data/repository/authentication_repository.dart b/lib/data/repository/authentication_repository.dart index 1d7f69b2..cbcfcede 100644 --- a/lib/data/repository/authentication_repository.dart +++ b/lib/data/repository/authentication_repository.dart @@ -121,8 +121,6 @@ class AuthenticationRepositoryImpl implements AuthenticationRepository { } } - static const String _googleWebClientId = "GOOGLE_WEB_CLIENT_ID"; - static const String _googleIosClientId = "GOOGLE_IOS_CLIENT_ID"; static const List _googleScopes = ['email', 'profile', 'openid']; @override From 533575b1a1cee217a216d02499d09654f458f50f Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 01:57:44 +0200 Subject: [PATCH 09/20] fix merge conflicts --- lib/core/di/cubit_di.dart | 14 +- lib/core/di/repository_di.dart | 4 +- .../repository/transaction_repository.dart | 2 +- .../repository/user_money_repository.dart | 3 +- lib/presentation/navigation/routes.g.dart | 196 ------------------ 5 files changed, 16 insertions(+), 203 deletions(-) delete mode 100644 lib/presentation/navigation/routes.g.dart diff --git a/lib/core/di/cubit_di.dart b/lib/core/di/cubit_di.dart index 178e7dcd..7f1aa41c 100644 --- a/lib/core/di/cubit_di.dart +++ b/lib/core/di/cubit_di.dart @@ -1,4 +1,3 @@ -import 'package:get_it/get_it.dart'; import 'package:moneyplus/domain/repository/account_repository.dart'; import 'package:moneyplus/domain/repository/authentication_repository.dart'; import 'package:moneyplus/domain/repository/statistics_repository.dart'; @@ -13,9 +12,9 @@ import 'package:moneyplus/presentation/statistics/cubit/statistics_cubit.dart'; import 'package:moneyplus/presentation/transactions/cubit/transaction_cubit.dart'; import 'package:moneyplus/presentation/trasnaction_details/trasnaction_details_cubit.dart'; +import '../../presentation/expense/cubit/add_expense_cubit.dart'; import 'injection.dart'; - void initCubitDI() { getIt.registerLazySingleton( () => AuthenticationValidator(), @@ -36,8 +35,17 @@ void initCubitDI() { () => AccountSetupCubit(getIt()), ); + getIt.registerFactory( + () => AddExpenseCubit( + transactionRepository: getIt(), + userMoneyRepository: getIt(), + ), + ); getIt.registerFactory( - () => AddIncomeCubit(repository: getIt()), + () => AddIncomeCubit( + transactionRepository: getIt(), + userMoneyRepository: getIt(), + ), ); getIt.registerFactory( diff --git a/lib/core/di/repository_di.dart b/lib/core/di/repository_di.dart index c630393b..d0707591 100644 --- a/lib/core/di/repository_di.dart +++ b/lib/core/di/repository_di.dart @@ -1,7 +1,7 @@ import '../../data/repository/account_repository.dart'; import '../../data/repository/authentication_repository.dart'; import '../../data/repository/statistics_repository_impl.dart'; -import '../../data/repository/transaction_repository_stub.dart'; +import '../../data/repository/transaction_repository.dart'; import '../../data/repository/user_money_repository.dart'; import '../../domain/repository/account_repository.dart'; import '../../domain/repository/authentication_repository.dart'; @@ -25,7 +25,7 @@ void initRepositoryDI() { () => AccountRepositoryImpl(supabaseService: getIt()), ); getIt.registerLazySingleton( - () => TransactionRepositoryStub(getIt()), + () => TransactionRepositoryImpl(service: getIt()), ); getIt.registerLazySingleton( () => StatisticsRepositoryImpl(supabaseService: getIt()), diff --git a/lib/data/repository/transaction_repository.dart b/lib/data/repository/transaction_repository.dart index 801368aa..889136d9 100644 --- a/lib/data/repository/transaction_repository.dart +++ b/lib/data/repository/transaction_repository.dart @@ -1,12 +1,12 @@ import 'package:moneyplus/core/errors/error_model.dart'; import 'package:moneyplus/core/errors/result.dart'; -import 'package:moneyplus/data/service/supabase_service.dart'; import 'package:moneyplus/domain/entity/transaction.dart'; import 'package:moneyplus/domain/entity/transaction_category.dart'; import 'package:moneyplus/domain/entity/transaction_type.dart'; import 'package:moneyplus/domain/repository/model/top_spending_category.dart'; import 'package:moneyplus/domain/repository/transaction_repository.dart'; +import '../../core/service/supabase_service.dart'; import '../../domain/entity/currency.dart'; diff --git a/lib/data/repository/user_money_repository.dart b/lib/data/repository/user_money_repository.dart index 18554700..36a8c19a 100644 --- a/lib/data/repository/user_money_repository.dart +++ b/lib/data/repository/user_money_repository.dart @@ -1,9 +1,10 @@ -import 'package:moneyplus/data/service/supabase_service.dart'; import 'package:moneyplus/domain/entity/currency.dart'; import 'package:moneyplus/domain/entity/transaction_category.dart'; import 'package:moneyplus/domain/repository/model/top_spending_category.dart'; import 'package:moneyplus/domain/repository/user_money_repository.dart'; +import '../../core/service/supabase_service.dart'; + class UserRepositoryImpl implements UserMoneyRepository { final SupabaseService service; diff --git a/lib/presentation/navigation/routes.g.dart b/lib/presentation/navigation/routes.g.dart deleted file mode 100644 index dd948d4d..00000000 --- a/lib/presentation/navigation/routes.g.dart +++ /dev/null @@ -1,196 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'routes.dart'; - -// ************************************************************************** -// GoRouterGenerator -// ************************************************************************** - -List get $appRoutes => [ - $onBoardingRoute, - $loginRoute, - $mainRoute, - $transactionDetailsRoute, - $statisticsRoute, - $forgetPasswordRoute, - $updatePasswordRoute, -]; - -RouteBase get $onBoardingRoute => - GoRouteData.$route(path: '/', factory: $OnBoardingRoute._fromState); - -mixin $OnBoardingRoute on GoRouteData { - static OnBoardingRoute _fromState(GoRouterState state) => - const OnBoardingRoute(); - - @override - String get location => GoRouteData.$location('/'); - - @override - void go(BuildContext context) => context.go(location); - - @override - Future push(BuildContext context) => context.push(location); - - @override - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - @override - void replace(BuildContext context) => context.replace(location); -} - -RouteBase get $loginRoute => - GoRouteData.$route(path: '/login', factory: $LoginRoute._fromState); - -mixin $LoginRoute on GoRouteData { - static LoginRoute _fromState(GoRouterState state) => const LoginRoute(); - - @override - String get location => GoRouteData.$location('/login'); - - @override - void go(BuildContext context) => context.go(location); - - @override - Future push(BuildContext context) => context.push(location); - - @override - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - @override - void replace(BuildContext context) => context.replace(location); -} - -RouteBase get $mainRoute => - GoRouteData.$route(path: '/main', factory: $MainRoute._fromState); - -mixin $MainRoute on GoRouteData { - static MainRoute _fromState(GoRouterState state) => const MainRoute(); - - @override - String get location => GoRouteData.$location('/main'); - - @override - void go(BuildContext context) => context.go(location); - - @override - Future push(BuildContext context) => context.push(location); - - @override - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - @override - void replace(BuildContext context) => context.replace(location); -} - -RouteBase get $transactionDetailsRoute => GoRouteData.$route( - path: '/transaction_details', - factory: $TransactionDetailsRoute._fromState, -); - -mixin $TransactionDetailsRoute on GoRouteData { - static TransactionDetailsRoute _fromState(GoRouterState state) => - TransactionDetailsRoute(state.uri.queryParameters['transaction-id']!); - - TransactionDetailsRoute get _self => this as TransactionDetailsRoute; - - @override - String get location => GoRouteData.$location( - '/transaction_details', - queryParams: {'transaction-id': _self.transactionId}, - ); - - @override - void go(BuildContext context) => context.go(location); - - @override - Future push(BuildContext context) => context.push(location); - - @override - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - @override - void replace(BuildContext context) => context.replace(location); -} - -RouteBase get $statisticsRoute => GoRouteData.$route( - path: '/statistics', - factory: $StatisticsRoute._fromState, -); - -mixin $StatisticsRoute on GoRouteData { - static StatisticsRoute _fromState(GoRouterState state) => - const StatisticsRoute(); - - @override - String get location => GoRouteData.$location('/statistics'); - - @override - void go(BuildContext context) => context.go(location); - - @override - Future push(BuildContext context) => context.push(location); - - @override - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - @override - void replace(BuildContext context) => context.replace(location); -} - -RouteBase get $forgetPasswordRoute => GoRouteData.$route( - path: '/forget_password', - factory: $ForgetPasswordRoute._fromState, -); - -mixin $ForgetPasswordRoute on GoRouteData { - static ForgetPasswordRoute _fromState(GoRouterState state) => - const ForgetPasswordRoute(); - - @override - String get location => GoRouteData.$location('/forget_password'); - - @override - void go(BuildContext context) => context.go(location); - - @override - Future push(BuildContext context) => context.push(location); - - @override - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - @override - void replace(BuildContext context) => context.replace(location); -} - -RouteBase get $updatePasswordRoute => GoRouteData.$route( - path: '/update_password', - factory: $UpdatePasswordRoute._fromState, -); - -mixin $UpdatePasswordRoute on GoRouteData { - static UpdatePasswordRoute _fromState(GoRouterState state) => - UpdatePasswordRoute(); - - @override - String get location => GoRouteData.$location('/update_password'); - - @override - void go(BuildContext context) => context.go(location); - - @override - Future push(BuildContext context) => context.push(location); - - @override - void pushReplacement(BuildContext context) => - context.pushReplacement(location); - - @override - void replace(BuildContext context) => context.replace(location); -} From 92020fc4958d2c4c0b3c87c77fbc5e7d060284ec Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 02:03:07 +0200 Subject: [PATCH 10/20] fix: Update branch names in workflows --- .github/workflows/android.yml | 4 ++-- .github/workflows/ios.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 4dc8d42f..486cc412 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -2,9 +2,9 @@ name: Flutter CI on: push: - branches: [ "development" , "main" ] + branches: [ "dev" , "main" ] pull_request: - branches: [ "development" , "main" ] + branches: [ "dev" , "main" ] workflow_dispatch: jobs: diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index b13dfe28..1b9afa8f 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -2,9 +2,9 @@ name: Flutter CI iOS on: push: - branches: [ "development", "main" ] + branches: [ "dev", "main" ] pull_request: - branches: [ "development", "main" ] + branches: [ "dev", "main" ] workflow_dispatch: jobs: From ce7a70236c831a859cf2d40c3ea221bdc91e281b Mon Sep 17 00:00:00 2001 From: mohamedshemees <72915905+mohamedshemees@users.noreply.github.com> Date: Thu, 19 Feb 2026 02:32:21 +0200 Subject: [PATCH 11/20] Add Firebase token to flutterfire configuration command --- .github/workflows/android.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 486cc412..33dfaff4 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -62,8 +62,8 @@ jobs: env: FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }} FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} - run: flutterfire configure --project=$FIREBASE_PROJECT_ID -y --platforms=android --out=lib/firebase_options.dart - + run: flutterfire configure --project=$FIREBASE_PROJECT_ID -y --platforms=android --out=lib/firebase_options.dart --token=$FIREBASE_TOKEN + - name: Configure Gradle memory run: | mkdir -p ~/.gradle @@ -106,4 +106,4 @@ jobs: firebase appdistribution:distribute build/app/outputs/flutter-apk/app-release.apk \ --app "$FIREBASE_APP_ID" \ --groups "closed-beta-testers" \ - --release-notes "Build from ${{ github.ref_name }} - Commit: ${{ github.sha }}" \ No newline at end of file + --release-notes "Build from ${{ github.ref_name }} - Commit: ${{ github.sha }}" From bea76da6866d70e077c8c89a40a50cd5663ff028 Mon Sep 17 00:00:00 2001 From: mohamedshemees <72915905+mohamedshemees@users.noreply.github.com> Date: Thu, 19 Feb 2026 02:56:23 +0200 Subject: [PATCH 12/20] Update Firebase options generation for Android Add android package name to Firebase configuration command --- .github/workflows/android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 33dfaff4..1c21ee05 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -62,7 +62,7 @@ jobs: env: FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }} FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} - run: flutterfire configure --project=$FIREBASE_PROJECT_ID -y --platforms=android --out=lib/firebase_options.dart --token=$FIREBASE_TOKEN + run: flutterfire configure --project=$FIREBASE_PROJECT_ID -y --platforms=android --out=lib/firebase_options.dart --token=$FIREBASE_TOKEN --android-package-name=com.pennypilot.moneyplus - name: Configure Gradle memory run: | From 7023005fb6426658a9220a9fffeee3a08986067a Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Thu, 19 Feb 2026 03:08:40 +0200 Subject: [PATCH 13/20] feat: Integrate Firebase Performance Monitoring --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index f76791d7..b3d1d84c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,6 +47,7 @@ dependencies: firebase_crashlytics: ^5.0.7 firebase_remote_config: ^6.1.4 firebase_analytics: ^12.1.1 + firebase_performance: ^0.11.1+4 dev_dependencies: flutter_test: @@ -64,7 +65,6 @@ flutter: assets: - assets/icons/ - assets/images/ - - secrets.env uses-material-design: true From 3d033f34036f0d55598e514e9350e7a159c6820c Mon Sep 17 00:00:00 2001 From: mohamedshemees <72915905+mohamedshemees@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:52:08 +0200 Subject: [PATCH 14/20] Update MinimumOSVersion to 15.0 --- ios/Flutter/AppFrameworkInfo.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 1dc6cf76..0d140800 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 13.0 + 15.0 From c5d4945ddcd4ab3d92ee6fa28dd7ef43f3059452 Mon Sep 17 00:00:00 2001 From: mohamedshemees <72915905+mohamedshemees@users.noreply.github.com> Date: Fri, 20 Feb 2026 01:03:54 +0200 Subject: [PATCH 15/20] Update project.pbxproj --- ios/Runner.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2f669c9e..84327a48 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -475,7 +475,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; From abfba928751652c49e350e0d291ef308dd8c37e4 Mon Sep 17 00:00:00 2001 From: mohamedshemees <72915905+mohamedshemees@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:47:24 +0200 Subject: [PATCH 16/20] Update iOS deployment target to 15.0 --- ios/Runner.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 84327a48..ef2d7198 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -606,7 +606,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -657,7 +657,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; From 8a9a6330aa9875158df4b850bcf96488575ad7fc Mon Sep 17 00:00:00 2001 From: mohamedshemees <72915905+mohamedshemees@users.noreply.github.com> Date: Fri, 20 Feb 2026 21:05:19 +0200 Subject: [PATCH 17/20] rollback iOS deployment target to 13.0 --- ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ef2d7198..2f669c9e 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -475,7 +475,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -606,7 +606,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -657,7 +657,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; From 611495d0f112faad7d44b098862dc7c651f54823 Mon Sep 17 00:00:00 2001 From: yousef_osama11 Date: Mon, 23 Feb 2026 20:46:34 +0200 Subject: [PATCH 18/20] refactor: update iOS deployment target to 15.0 and configure Podfile --- ios/Podfile | 46 ++++++++++++++++++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 ios/Podfile diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..c7a0964b --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,46 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '15.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0' + end + end +end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2f669c9e..82128e75 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -657,7 +657,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; From db9ccad280daca9e3ddb262bf1c563191608cd3a Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Wed, 25 Feb 2026 19:28:58 +0200 Subject: [PATCH 19/20] fix merge conflicts --- lib/core/di/cubit_di.dart | 8 ++++++++ lib/core/di/injection.dart | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/core/di/cubit_di.dart b/lib/core/di/cubit_di.dart index 7f1aa41c..7265b750 100644 --- a/lib/core/di/cubit_di.dart +++ b/lib/core/di/cubit_di.dart @@ -5,6 +5,7 @@ import 'package:moneyplus/domain/repository/transaction_repository.dart'; import 'package:moneyplus/domain/repository/user_money_repository.dart'; import 'package:moneyplus/domain/validator/authentication_validator.dart'; import 'package:moneyplus/presentation/account_setup/cubit/account_setup_cubit.dart'; +import 'package:moneyplus/presentation/createAccount/cubit/create_account_cubit.dart'; import 'package:moneyplus/presentation/home/cubit/home_cubit.dart'; import 'package:moneyplus/presentation/income/cubit/add_income_cubit.dart'; import 'package:moneyplus/presentation/login/cubit/login_cubit.dart'; @@ -62,4 +63,11 @@ void initCubitDI() { transactionRepository: getIt(), ), ); + + getIt.registerFactory( + () => CreateAccountCubit( + getIt(), + getIt(), + ), + ); } diff --git a/lib/core/di/injection.dart b/lib/core/di/injection.dart index a276fe85..cb77a090 100644 --- a/lib/core/di/injection.dart +++ b/lib/core/di/injection.dart @@ -6,7 +6,6 @@ import 'package:moneyplus/core/di/service_di.dart'; final getIt = GetIt.instance; void initDI() { - // Initialize services, repositories and cubits from other files initServiceDI(); initRepositoryDI(); initCubitDI(); From 01fde084851efff496d50b8b3bc3d639a904053b Mon Sep 17 00:00:00 2001 From: mohamedshemees Date: Wed, 25 Feb 2026 19:42:36 +0200 Subject: [PATCH 20/20] fix: update ios workflow to include dependency caching and firebase configuration --- .github/workflows/ios.yml | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 1b9afa8f..18348a34 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -22,11 +22,45 @@ jobs: channel: 'stable' cache: true + - name: Cache pub dependencies + uses: actions/cache@v3 + with: + path: | + ${{ env.PUB_CACHE }} + ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} + restore-keys: | + ${{ runner.os }}-pub- + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install Firebase CLI + run: npm install -g firebase-tools + + - name: Install FlutterFire CLI + run: dart pub global activate flutterfire_cli + - name: Install dependencies run: flutter pub get - name: Generate local code run: flutter pub run build_runner build --delete-conflicting-outputs + - name: Generate Firebase Options + env: + FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }} + FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + run: | + flutterfire configure \ + --project=$FIREBASE_PROJECT_ID \ + -y \ + --platforms=ios \ + --out=lib/firebase_options.dart \ + --token=$FIREBASE_TOKEN \ + --ios-bundle-id=com.pennypilot.moneyplus + - name: Build iOS (simulator) - run: flutter build ios --simulator \ No newline at end of file + run: flutter build ios --simulator --no-codesign \ No newline at end of file