From 9b03ce4549d7c6db64fcaa499aa1f36e53e53d44 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 20 Oct 2026 14:04:12 -0400 Subject: [PATCH 1/6] unfocus keyboard on app hide --- lib/controllers/app_paused.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/controllers/app_paused.dart b/lib/controllers/app_paused.dart index 09a5b0a..25b77ca 100644 --- a/lib/controllers/app_paused.dart +++ b/lib/controllers/app_paused.dart @@ -5,6 +5,7 @@ class AppPausedController extends Notifier { @override bool build() { final listener = AppLifecycleListener( + onHide: () => FocusManager.instance.primaryFocus?.unfocus(), onPause: () => state = true, onResume: () => state = false, ); From 1a4d391a6c468d53c135c49840ffdb672f5237e7 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 20 Oct 2026 14:27:35 -0400 Subject: [PATCH 2/6] attempt another fix for keyboard issues --- .../mobile_requester_app/MainActivity.kt | 5 ----- .../nexus/federated/nexus/MainActivity.kt | 19 +++++++++++++++++++ lib/controllers/app_paused.dart | 1 - 3 files changed, 19 insertions(+), 6 deletions(-) delete mode 100644 android/app/src/main/kotlin/com/example/mobile_requester_app/MainActivity.kt create mode 100644 android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt diff --git a/android/app/src/main/kotlin/com/example/mobile_requester_app/MainActivity.kt b/android/app/src/main/kotlin/com/example/mobile_requester_app/MainActivity.kt deleted file mode 100644 index 25aab2f..0000000 --- a/android/app/src/main/kotlin/com/example/mobile_requester_app/MainActivity.kt +++ /dev/null @@ -1,5 +0,0 @@ -package nexus.federated.nexus - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() diff --git a/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt new file mode 100644 index 0000000..a243e36 --- /dev/null +++ b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt @@ -0,0 +1,19 @@ +package nexus.federated.nexus + +import android.view.View +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + if (!hasFocus) return + + // Flutter swallows inset updates while it thinks a keyboard animation is + // running, and only stops when the animation ends. If the app is + // backgrounded mid-animation, it can miss the end and keep the keyboard's + // space reserved, so hand the view the real insets directly. + val view = findViewById(FLUTTER_VIEW_ID) ?: return + val insets = view.rootWindowInsets ?: return + view.onApplyWindowInsets(insets) + } +} diff --git a/lib/controllers/app_paused.dart b/lib/controllers/app_paused.dart index 25b77ca..09a5b0a 100644 --- a/lib/controllers/app_paused.dart +++ b/lib/controllers/app_paused.dart @@ -5,7 +5,6 @@ class AppPausedController extends Notifier { @override bool build() { final listener = AppLifecycleListener( - onHide: () => FocusManager.instance.primaryFocus?.unfocus(), onPause: () => state = true, onResume: () => state = false, ); From 426a459fb11a280662075110099904c05a80879e Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 20 Oct 2026 14:56:08 -0400 Subject: [PATCH 3/6] another test --- .../nexus/federated/nexus/MainActivity.kt | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt index a243e36..ea5c3c9 100644 --- a/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt +++ b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt @@ -1,19 +1,39 @@ package nexus.federated.nexus +import android.os.Build +import android.os.Bundle import android.view.View +import android.view.WindowInsets +import android.view.WindowInsetsAnimation import io.flutter.embedding.android.FlutterActivity class MainActivity: FlutterActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return + + window.decorView.setWindowInsetsAnimationCallback( + object : WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { + override fun onProgress( + insets: WindowInsets, + runningAnimations: List, + ) = insets + + override fun onEnd(animation: WindowInsetsAnimation) { + if ((animation.typeMask and WindowInsets.Type.ime()) != 0) reapplyInsets() + } + }, + ) + } + override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) - if (!hasFocus) return + if (hasFocus && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) reapplyInsets() + } - // Flutter swallows inset updates while it thinks a keyboard animation is - // running, and only stops when the animation ends. If the app is - // backgrounded mid-animation, it can miss the end and keep the keyboard's - // space reserved, so hand the view the real insets directly. + // Posted so it runs after Flutter has applied any insets it deferred. + private fun reapplyInsets() { val view = findViewById(FLUTTER_VIEW_ID) ?: return - val insets = view.rootWindowInsets ?: return - view.onApplyWindowInsets(insets) + view.post { view.rootWindowInsets?.let(view::onApplyWindowInsets) } } } From 070a9ede8f517253afcfefebe7d102fc027a5e6c Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 20 Oct 2026 15:13:38 -0400 Subject: [PATCH 4/6] add a TODO to remove mainactivity workaround once flutter bug fixed --- .../app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt index ea5c3c9..de49362 100644 --- a/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt +++ b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt @@ -7,6 +7,7 @@ import android.view.WindowInsets import android.view.WindowInsetsAnimation import io.flutter.embedding.android.FlutterActivity +// TODO: Remove the inset workaround once https://github.com/flutter/flutter/issues/191156 is fixed class MainActivity: FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) From 78162efbde8f4724bee87415ffd1ed4b1a678099 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 20 Oct 2026 15:15:42 -0400 Subject: [PATCH 5/6] move MainActivity to correct directory --- .../federated/nexus}/MainActivity.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename android/app/src/main/kotlin/{com/example/mobile_requester_app => nexus/federated/nexus}/MainActivity.kt (100%) diff --git a/android/app/src/main/kotlin/com/example/mobile_requester_app/MainActivity.kt b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt similarity index 100% rename from android/app/src/main/kotlin/com/example/mobile_requester_app/MainActivity.kt rename to android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt From 80197aff00dd66e11751632afbe5b3702df059f1 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 20 Oct 2026 15:17:34 -0400 Subject: [PATCH 6/6] add keyboard inset workaround to MainActivity Should be reverted once https://github.com/flutter/flutter/issues/191156 is fixed --- .../nexus/federated/nexus/MainActivity.kt | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt index 25aab2f..f2b0f68 100644 --- a/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt +++ b/android/app/src/main/kotlin/nexus/federated/nexus/MainActivity.kt @@ -1,5 +1,40 @@ package nexus.federated.nexus +import android.os.Build +import android.os.Bundle +import android.view.View +import android.view.WindowInsets +import android.view.WindowInsetsAnimation import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +// TODO: Revert this commit once https://github.com/flutter/flutter/issues/191156 is fixed +class MainActivity: FlutterActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return + + window.decorView.setWindowInsetsAnimationCallback( + object : WindowInsetsAnimation.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { + override fun onProgress( + insets: WindowInsets, + runningAnimations: List, + ) = insets + + override fun onEnd(animation: WindowInsetsAnimation) { + if ((animation.typeMask and WindowInsets.Type.ime()) != 0) reapplyInsets() + } + }, + ) + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + if (hasFocus && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) reapplyInsets() + } + + // Posted so it runs after Flutter has applied any insets it deferred. + private fun reapplyInsets() { + val view = findViewById(FLUTTER_VIEW_ID) ?: return + view.post { view.rootWindowInsets?.let(view::onApplyWindowInsets) } + } +}