diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml
index 3da7cbe107..e522854059 100644
--- a/.github/workflows/build-mobile.yml
+++ b/.github/workflows/build-mobile.yml
@@ -37,15 +37,15 @@ jobs:
 
       - uses: actions/setup-java@v4
         with:
-          distribution: "zulu"
-          java-version: "11.0.21+9"
-          cache: "gradle"
+          distribution: 'zulu'
+          java-version: '17'
+          cache: 'gradle'
 
       - name: Setup Flutter SDK
         uses: subosito/flutter-action@v2
         with:
-          channel: "stable"
-          flutter-version: "3.19.3"
+          channel: 'stable'
+          flutter-version: '3.19.3'
           cache: true
 
       - name: Create the Keystore
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d74c961393..51b947418e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -208,7 +208,7 @@ jobs:
         uses: subosito/flutter-action@v2
         with:
           channel: 'stable'
-          flutter-version: '3.16.9'
+          flutter-version: '3.19.3'
       - name: Run tests
         working-directory: ./mobile
         run: flutter test -j 1
diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle
index 96d2db23f5..85d6206e1f 100644
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -1,14 +1,14 @@
+plugins {
+    id "com.android.application"
+    id "kotlin-android"
+    id "dev.flutter.flutter-gradle-plugin"
+    id "kotlin-kapt"
+}
+
 def localProperties = new Properties()
 def localPropertiesFile = rootProject.file('local.properties')
 if (localPropertiesFile.exists()) {
-    localPropertiesFile.withReader('UTF-8') { reader ->
-        localProperties.load(reader)
-    }
-}
-
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
-    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+    localPropertiesFile.withInputStream { localProperties.load(it) }
 }
 
 def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
@@ -21,18 +21,12 @@ if (flutterVersionName == null) {
     flutterVersionName = '1.0'
 }
 
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
 def keystoreProperties = new Properties()
 def keystorePropertiesFile = rootProject.file('key.properties')
 if (keystorePropertiesFile.exists()) {
-    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+    keystorePropertiesFile.withInputStream { keystoreProperties.load(it) }
 }
 
-
 android {
     compileSdkVersion 34
 
@@ -50,7 +44,6 @@ android {
     }
 
     defaultConfig {
-        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "app.alextran.immich"
         minSdkVersion 26
         targetSdkVersion 33
@@ -88,6 +81,13 @@ flutter {
 }
 
 dependencies {
+    def kotlin_version = '1.9.23'
+    def kotlin_coroutines_version = '1.8.0'
+    def work_version = '2.9.0'
+    def concurrent_version = '1.1.0'
+    def guava_version = '33.1.0-android'
+    def glide_version = '4.16.0'
+
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
     implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
     implementation "androidx.work:work-runtime-ktx:$work_version"
diff --git a/mobile/android/app/src/main/kotlin/com/example/mobile/BackupWorker.kt b/mobile/android/app/src/main/kotlin/com/example/mobile/BackupWorker.kt
index 660e1d55ba..b6b78c2cba 100644
--- a/mobile/android/app/src/main/kotlin/com/example/mobile/BackupWorker.kt
+++ b/mobile/android/app/src/main/kotlin/com/example/mobile/BackupWorker.kt
@@ -276,7 +276,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
         private const val NOTIFICATION_CHANNEL_ERROR_ID = "immich/backgroundServiceError"
         private const val NOTIFICATION_DEFAULT_TITLE = "Immich"
         private const val NOTIFICATION_ID = 1
-        private const val NOTIFICATION_ERROR_ID = 2 
+        private const val NOTIFICATION_ERROR_ID = 2
         private const val NOTIFICATION_DETAIL_ID = 3
         private const val ONE_MINUTE = 60000L
 
@@ -304,7 +304,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
                 val workInfoList = workInfoFuture.get(1000, TimeUnit.MILLISECONDS)
                 if (workInfoList != null) {
                     for (workInfo in workInfoList) {
-                        if (workInfo.getState() == WorkInfo.State.ENQUEUED) {
+                        if (workInfo.state == WorkInfo.State.ENQUEUED) {
                             val workRequest = buildWorkRequest(requireWifi, requireCharging)
                             wm.enqueueUniqueWork(TASK_NAME_BACKUP, ExistingWorkPolicy.REPLACE, workRequest)
                             Log.d(TAG, "updateBackupWorker updated BackupWorker constraints")
@@ -346,7 +346,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
                 .setRequiresBatteryNotLow(true)
                 .setRequiresCharging(requireCharging)
                 .build();
-            
+
             val work = OneTimeWorkRequest.Builder(BackupWorker::class.java)
                 .setConstraints(constraints)
                 .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, ONE_MINUTE, TimeUnit.MILLISECONDS)
@@ -359,4 +359,4 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
     }
 }
 
-private const val TAG = "BackupWorker"
\ No newline at end of file
+private const val TAG = "BackupWorker"
diff --git a/mobile/android/build.gradle b/mobile/android/build.gradle
index 4dacde5a9d..5e374c9f64 100644
--- a/mobile/android/build.gradle
+++ b/mobile/android/build.gradle
@@ -1,21 +1,3 @@
-buildscript {
-    ext.kotlin_version = '1.8.20'
-    ext.kotlin_coroutines_version = '1.7.1'
-    ext.work_version = '2.7.1'
-    ext.concurrent_version = '1.1.0'
-    ext.guava_version = '33.0.0-android'
-    ext.glide_version = '4.14.2'
-    repositories {
-        google()
-        mavenCentral()
-    }
-
-    dependencies {
-        classpath 'com.android.tools.build:gradle:7.4.2'
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-    }
-}
-
 allprojects {
     repositories {
         google()
@@ -34,3 +16,7 @@ subprojects {
 tasks.register("clean", Delete) {
     delete rootProject.buildDir
 }
+
+tasks.named('wrapper') {
+    distributionType = Wrapper.DistributionType.ALL
+}
\ No newline at end of file
diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.properties b/mobile/android/gradle/wrapper/gradle-wrapper.properties
index 7787882b74..6357330c9e 100644
--- a/mobile/android/gradle/wrapper/gradle-wrapper.properties
+++ b/mobile/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
-distributionSha256Sum=6001aba9b2204d26fa25a5800bb9382cf3ee01ccb78fe77317b2872336eb2f80
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-all.zip
+distributionSha256Sum=fe696c020f241a5f69c30f763c5a7f38eec54b490db19cd2b0962dda420d7d12
\ No newline at end of file
diff --git a/mobile/android/settings.gradle b/mobile/android/settings.gradle
index 44e62bcf06..7ea6533b65 100644
--- a/mobile/android/settings.gradle
+++ b/mobile/android/settings.gradle
@@ -1,11 +1,26 @@
-include ':app'
+pluginManagement {
+    def flutterSdkPath = {
+        def properties = new Properties()
+        file("local.properties").withInputStream { properties.load(it) }
+        def flutterSdkPath = properties.getProperty("flutter.sdk")
+        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+        return flutterSdkPath
+    }()
 
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
 
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+    repositories {
+        google()
+        mavenCentral()
+        gradlePluginPortal()
+    }
+}
 
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+plugins {
+    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+    id "com.android.application" version "7.4.2" apply false
+    id "org.jetbrains.kotlin.android" version "1.9.23" apply false
+    id "org.jetbrains.kotlin.kapt" version "1.9.23" apply false
+}
+
+include ":app"
diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock
index 6bf2b09026..54648fd20b 100644
--- a/mobile/pubspec.lock
+++ b/mobile/pubspec.lock
@@ -1804,5 +1804,5 @@ packages:
     source: hosted
     version: "3.1.2"
 sdks:
-  dart: ">=3.2.0 <4.0.0"
+  dart: ">=3.3.0 <4.0.0"
   flutter: ">=3.16.0"
diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml
index 828c9a63b7..8ae3a21318 100644
--- a/mobile/pubspec.yaml
+++ b/mobile/pubspec.yaml
@@ -5,7 +5,7 @@ publish_to: 'none'
 version: 1.102.3+136
 
 environment:
-  sdk: '>=3.0.0 <4.0.0'
+  sdk: '>=3.3.0 <4.0.0'
 
 dependencies:
   flutter: