Skip to content

Commit 3cf7dc5

Browse files
[Super Keyboard] - Fix: Samsung keyboard toolbar height report issue (#2890)
1 parent 0dc17dd commit 3cf7dc5

File tree

4 files changed

+118
-81
lines changed

4 files changed

+118
-81
lines changed

super_keyboard/android/src/main/kotlin/com/flutterbountyhunters/superkeyboard/super_keyboard/SuperKeyboardPlugin.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.flutterbountyhunters.superkeyboard.super_keyboard
22

33
import android.app.Activity
4+
import android.os.Handler
5+
import android.os.Looper
46
import android.view.View
57
import android.view.ViewGroup
68
import android.view.inputmethod.InputMethodManager
@@ -203,9 +205,23 @@ class SuperKeyboardPlugin: FlutterPlugin, ActivityAware, DefaultLifecycleObserve
203205
// Report whether the keyboard has fully opened or fully closed.
204206
SuperKeyboardLog.i("super_keyboard", "Insets animation callback - onEnd - current keyboard state: $keyboardState")
205207
if (keyboardState == KeyboardState.Opening) {
206-
SuperKeyboardLog.i("super_keyboard", "Sending new keyboard state: open")
207-
keyboardState = KeyboardState.Open
208-
sendMessageKeyboardOpened()
208+
// It was discovered that on at least some Samsung devices, such as Galaxy S24 on API 14
209+
// and API 16, `onEnd()` runs a frame before the final keyboard inset height is applied.
210+
// Therefore, we always wait a frame to report the completion of the keyboard opening.
211+
Handler(Looper.getMainLooper()).post {
212+
SuperKeyboardLog.i("super_keyboard", "Sending new keyboard state: open")
213+
214+
// Update our cached measurements.
215+
val insets = ViewCompat.getRootWindowInsets(binding.activity.window.decorView)
216+
if (insets != null) {
217+
// Note: I don't ever expect insets to be null here, but technically it's possible.
218+
imeHeightInDpi = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom / dpi
219+
bottomPaddingInDpi = insets.getInsets(WindowInsetsCompat.Type.mandatorySystemGestures()).bottom / dpi
220+
}
221+
222+
keyboardState = KeyboardState.Open
223+
sendMessageKeyboardOpened()
224+
}
209225
} else if (keyboardState == KeyboardState.Closing) {
210226
SuperKeyboardLog.i("super_keyboard", "Sending new keyboard state: closing")
211227
keyboardState = KeyboardState.Closed

super_keyboard/example/android/settings.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ pluginManagement {
1818

1919
plugins {
2020
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21-
id "com.android.application" version "8.2.2" apply false
22-
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
21+
id "com.android.application" version '8.6.0' apply false
22+
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
2323
}
2424

2525
include ":app"

super_keyboard/example/lib/main.dart

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -46,70 +46,91 @@ class _SuperKeyboardDemoAppState extends State<SuperKeyboardDemoApp> {
4646
return MaterialApp(
4747
home: Scaffold(
4848
resizeToAvoidBottomInset: defaultTargetPlatform != TargetPlatform.android,
49-
body: Center(
50-
child: ConstrainedBox(
51-
constraints: const BoxConstraints(maxWidth: 300),
52-
child: Column(
53-
mainAxisSize: MainAxisSize.min,
54-
children: [
55-
_buildKeyboardStateIcon(),
56-
const SizedBox(height: 12),
57-
SuperKeyboardBuilder(
58-
builder: (context, keyboardState) {
59-
return Text("Keyboard state: $_keyboardState");
60-
},
49+
body: Stack(
50+
children: [
51+
// Placeholder "X" behind content to show what we think is above the keyboard.
52+
SuperKeyboardBuilder(builder: (context, keyboardState) {
53+
final keyboardHeight = keyboardState.keyboardHeight ?? 0;
54+
55+
return Positioned(
56+
left: 0,
57+
right: 0,
58+
top: 0,
59+
bottom: keyboardHeight > 0 ? keyboardHeight - MediaQuery.paddingOf(context).bottom : 0,
60+
child: const Opacity(
61+
opacity: 0.1,
62+
child: Placeholder(),
6163
),
62-
const SizedBox(height: 12),
63-
ValueListenableBuilder(
64-
valueListenable: SuperKeyboard.instance.mobileGeometry,
65-
builder: (context, value, child) {
66-
return Text(
67-
"Keyboard height: ${value.keyboardHeight != null ? "${value.keyboardHeight!.toInt()}" : "???"}");
68-
},
69-
),
70-
const SizedBox(height: 48),
71-
TextField(
72-
focusNode: _textFieldFocusNode,
73-
decoration: const InputDecoration(
74-
hintText: "Type some text",
64+
);
65+
}),
66+
Positioned.fill(
67+
child: Center(
68+
child: ConstrainedBox(
69+
constraints: const BoxConstraints(maxWidth: 300),
70+
child: Column(
71+
mainAxisSize: MainAxisSize.min,
72+
children: [
73+
_buildKeyboardStateIcon(),
74+
const SizedBox(height: 12),
75+
SuperKeyboardBuilder(
76+
builder: (context, keyboardState) {
77+
return Text("Keyboard state: $_keyboardState");
78+
},
79+
),
80+
const SizedBox(height: 12),
81+
ValueListenableBuilder(
82+
valueListenable: SuperKeyboard.instance.mobileGeometry,
83+
builder: (context, value, child) {
84+
return Text(
85+
"Keyboard height: ${value.keyboardHeight != null ? "${value.keyboardHeight!.toInt()}" : "???"}");
86+
},
87+
),
88+
const SizedBox(height: 48),
89+
TextField(
90+
focusNode: _textFieldFocusNode,
91+
decoration: const InputDecoration(
92+
hintText: "Type some text",
93+
),
94+
onTapOutside: (_) {
95+
if (_closeOnOutsideTap) {
96+
FocusManager.instance.primaryFocus?.unfocus();
97+
}
98+
},
99+
),
100+
const SizedBox(height: 16),
101+
ElevatedButton(
102+
onPressed: () {
103+
// ignore: avoid_print
104+
print("Requesting text field focus");
105+
_textFieldFocusNode.requestFocus();
106+
107+
WidgetsBinding.instance.addPostFrameCallback((_) {
108+
// ignore: avoid_print
109+
print("Unfocusing text field");
110+
_textFieldFocusNode.unfocus();
111+
});
112+
},
113+
child: const Text("Open/Close Rapidly"),
114+
),
115+
_buildCloseOnFocusOption(),
116+
_buildFlutterLoggingOption(),
117+
_buildPlatformLoggingOption(),
118+
ValueListenableBuilder(
119+
valueListenable: SuperKeyboard.instance.mobileGeometry,
120+
builder: (context, value, child) {
121+
if (value.keyboardHeight == null) {
122+
return const SizedBox();
123+
}
124+
125+
return SizedBox(height: value.keyboardHeight! / MediaQuery.of(context).devicePixelRatio);
126+
},
127+
),
128+
],
75129
),
76-
onTapOutside: (_) {
77-
if (_closeOnOutsideTap) {
78-
FocusManager.instance.primaryFocus?.unfocus();
79-
}
80-
},
81-
),
82-
const SizedBox(height: 16),
83-
ElevatedButton(
84-
onPressed: () {
85-
// ignore: avoid_print
86-
print("Requesting text field focus");
87-
_textFieldFocusNode.requestFocus();
88-
89-
WidgetsBinding.instance.addPostFrameCallback((_) {
90-
// ignore: avoid_print
91-
print("Unfocusing text field");
92-
_textFieldFocusNode.unfocus();
93-
});
94-
},
95-
child: const Text("Open/Close Rapidly"),
96-
),
97-
_buildCloseOnFocusOption(),
98-
_buildFlutterLoggingOption(),
99-
_buildPlatformLoggingOption(),
100-
ValueListenableBuilder(
101-
valueListenable: SuperKeyboard.instance.mobileGeometry,
102-
builder: (context, value, child) {
103-
if (value.keyboardHeight == null) {
104-
return const SizedBox();
105-
}
106-
107-
return SizedBox(height: value.keyboardHeight! / MediaQuery.of(context).devicePixelRatio);
108-
},
109130
),
110-
],
131+
),
111132
),
112-
),
133+
],
113134
),
114135
),
115136
);

super_keyboard/example/pubspec.lock

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ packages:
5353
dependency: transitive
5454
description:
5555
name: fake_async
56-
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
56+
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
5757
url: "https://pub.dev"
5858
source: hosted
59-
version: "1.3.2"
59+
version: "1.3.3"
6060
file:
6161
dependency: transitive
6262
description:
@@ -118,26 +118,26 @@ packages:
118118
dependency: transitive
119119
description:
120120
name: leak_tracker
121-
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
121+
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
122122
url: "https://pub.dev"
123123
source: hosted
124-
version: "10.0.8"
124+
version: "11.0.2"
125125
leak_tracker_flutter_testing:
126126
dependency: transitive
127127
description:
128128
name: leak_tracker_flutter_testing
129-
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
129+
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
130130
url: "https://pub.dev"
131131
source: hosted
132-
version: "3.0.9"
132+
version: "3.0.10"
133133
leak_tracker_testing:
134134
dependency: transitive
135135
description:
136136
name: leak_tracker_testing
137-
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
137+
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
138138
url: "https://pub.dev"
139139
source: hosted
140-
version: "3.0.1"
140+
version: "3.0.2"
141141
lints:
142142
dependency: transitive
143143
description:
@@ -174,10 +174,10 @@ packages:
174174
dependency: transitive
175175
description:
176176
name: meta
177-
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
177+
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
178178
url: "https://pub.dev"
179179
source: hosted
180-
version: "1.16.0"
180+
version: "1.17.0"
181181
path:
182182
dependency: transitive
183183
description:
@@ -253,7 +253,7 @@ packages:
253253
path: ".."
254254
relative: true
255255
source: path
256-
version: "0.1.1"
256+
version: "0.3.0"
257257
sync_http:
258258
dependency: transitive
259259
description:
@@ -274,18 +274,18 @@ packages:
274274
dependency: transitive
275275
description:
276276
name: test_api
277-
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
277+
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
278278
url: "https://pub.dev"
279279
source: hosted
280-
version: "0.7.4"
280+
version: "0.7.7"
281281
vector_math:
282282
dependency: transitive
283283
description:
284284
name: vector_math
285-
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
285+
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
286286
url: "https://pub.dev"
287287
source: hosted
288-
version: "2.1.4"
288+
version: "2.2.0"
289289
vm_service:
290290
dependency: transitive
291291
description:
@@ -303,5 +303,5 @@ packages:
303303
source: hosted
304304
version: "3.0.3"
305305
sdks:
306-
dart: ">=3.7.0-0 <4.0.0"
306+
dart: ">=3.8.0-0 <4.0.0"
307307
flutter: ">=3.27.0"

0 commit comments

Comments
 (0)