Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 933f811

Browse files
authored
Remove extraneous window inset call on IME animation (#21213)
1 parent 6a66d63 commit 933f811

File tree

2 files changed

+35
-13
lines changed

2 files changed

+35
-13
lines changed

shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,17 @@ class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback
199199

200200
private View view;
201201
private WindowInsets lastWindowInsets;
202-
private boolean started = false;
202+
// True when an animation that matches deferredInsetTypes is active.
203+
//
204+
// While this is active, this class will capture the initial window inset
205+
// sent into lastWindowInsets by flagging needsSave to true, and will hold
206+
// onto the intitial inset until the animation is completed, when it will
207+
// re-dispatch the inset change.
208+
private boolean animating = false;
209+
// When an animation begins, android sends a WindowInset with the final
210+
// state of the animation. When needsSave is true, we know to capture this
211+
// initial WindowInset.
212+
private boolean needsSave = false;
203213

204214
ImeSyncDeferringInsetsCallback(
205215
@NonNull View view, int overlayInsetTypes, int deferredInsetTypes) {
@@ -212,34 +222,38 @@ class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback
212222
@Override
213223
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
214224
this.view = view;
215-
if (started) {
225+
if (needsSave) {
226+
// Store the view and insets for us in onEnd() below. This captured inset
227+
// is not part of the animation and instead, represents the final state
228+
// of the inset after the animation is completed. Thus, we defer the processing
229+
// of this WindowInset until the animation completes.
230+
lastWindowInsets = windowInsets;
231+
needsSave = false;
232+
}
233+
if (animating) {
216234
// While animation is running, we consume the insets to prevent disrupting
217235
// the animation, which skips this implementation and calls the view's
218236
// onApplyWindowInsets directly to avoid being consumed here.
219237
return WindowInsets.CONSUMED;
220238
}
221239

222-
// Store the view and insets for us in onEnd() below
223-
lastWindowInsets = windowInsets;
224-
225240
// If no animation is happening, pass the insets on to the view's own
226241
// inset handling.
227242
return view.onApplyWindowInsets(windowInsets);
228243
}
229244

230245
@Override
231-
public WindowInsetsAnimation.Bounds onStart(
232-
WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
246+
public void onPrepare(WindowInsetsAnimation animation) {
233247
if ((animation.getTypeMask() & deferredInsetTypes) != 0) {
234-
started = true;
248+
animating = true;
249+
needsSave = true;
235250
}
236-
return bounds;
237251
}
238252

239253
@Override
240254
public WindowInsets onProgress(
241255
WindowInsets insets, List<WindowInsetsAnimation> runningAnimations) {
242-
if (!started) {
256+
if (!animating || needsSave) {
243257
return insets;
244258
}
245259
boolean matching = false;
@@ -280,10 +294,10 @@ public WindowInsets onProgress(
280294

281295
@Override
282296
public void onEnd(WindowInsetsAnimation animation) {
283-
if (started && (animation.getTypeMask() & deferredInsetTypes) != 0) {
297+
if (animating && (animation.getTypeMask() & deferredInsetTypes) != 0) {
284298
// If we deferred the IME insets and an IME animation has finished, we need to reset
285299
// the flags
286-
started = false;
300+
animating = false;
287301

288302
// And finally dispatch the deferred insets to the view now.
289303
// Ideally we would just call view.requestApplyInsets() and let the normal dispatch

shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,8 @@ public void ime_windowInsetsSync() {
669669
WindowInsets.Builder builder = new WindowInsets.Builder();
670670
WindowInsets noneInsets = builder.build();
671671

672+
// imeInsets0, 1, and 2 contain unique IME bottom insets, and are used
673+
// to distinguish which insets were sent at each stage.
672674
builder.setInsets(WindowInsets.Type.ime(), Insets.of(0, 0, 0, 100));
673675
builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 40));
674676
WindowInsets imeInsets0 = builder.build();
@@ -677,6 +679,10 @@ public void ime_windowInsetsSync() {
677679
builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 40));
678680
WindowInsets imeInsets1 = builder.build();
679681

682+
builder.setInsets(WindowInsets.Type.ime(), Insets.of(0, 0, 0, 50));
683+
builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 40));
684+
WindowInsets imeInsets2 = builder.build();
685+
680686
builder.setInsets(WindowInsets.Type.ime(), Insets.of(0, 0, 0, 200));
681687
builder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(10, 10, 10, 0));
682688
WindowInsets deferredInsets = builder.build();
@@ -696,6 +702,8 @@ public void ime_windowInsetsSync() {
696702
imeSyncCallback.onPrepare(animation);
697703
imeSyncCallback.onApplyWindowInsets(testView, deferredInsets);
698704
imeSyncCallback.onStart(animation, null);
705+
// Only the final state call is saved, extra calls are passed on.
706+
imeSyncCallback.onApplyWindowInsets(testView, imeInsets2);
699707

700708
verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture());
701709
// No change, as deferredInset is stored to be passed in onEnd()
@@ -723,7 +731,7 @@ public void ime_windowInsetsSync() {
723731
imeSyncCallback.onEnd(animation);
724732

725733
verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture());
726-
// Values should be of deferredInsets
734+
// Values should be of deferredInsets, not imeInsets2
727735
assertEquals(0, viewportMetricsCaptor.getValue().paddingBottom);
728736
assertEquals(10, viewportMetricsCaptor.getValue().paddingTop);
729737
assertEquals(200, viewportMetricsCaptor.getValue().viewInsetBottom);

0 commit comments

Comments
 (0)