diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 5bb17250c3c..fbfaa59265f 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -1594,12 +1594,20 @@ public class FlutterView extends FrameLayout @Override public void onProvideAutofillVirtualStructure(@NonNull ViewStructure structure, int flags) { super.onProvideAutofillVirtualStructure(structure, flags); - textInputPlugin.onProvideAutofillVirtualStructure(structure, flags); + // Defensive null check to prevent NPE when textInputPlugin is not yet initialized + // (e.g., when attachToEngineAutomatically is false). + if (textInputPlugin != null) { + textInputPlugin.onProvideAutofillVirtualStructure(structure, flags); + } } @Override public void autofill(@NonNull SparseArray values) { - textInputPlugin.autofill(values); + // Defensive null check to prevent NPE when textInputPlugin is not yet initialized + // (e.g., when attachToEngineAutomatically is false). + if (textInputPlugin != null) { + textInputPlugin.autofill(values); + } } @Override diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index 31a3e9cd336..8cdb8499b56 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -39,11 +39,14 @@ import android.media.ImageReader; import android.os.Build; import android.provider.Settings; import android.util.DisplayMetrics; +import android.util.SparseArray; import android.view.DisplayCutout; import android.view.RoundedCorner; import android.view.Surface; import android.view.View; +import android.view.ViewStructure; import android.view.WindowInsets; +import android.view.autofill.AutofillValue; import android.widget.FrameLayout; import androidx.core.util.Consumer; import androidx.test.core.app.ActivityScenario; @@ -59,6 +62,7 @@ import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.embedding.engine.systemchannels.SettingsChannel; import io.flutter.plugin.platform.PlatformViewsController; import io.flutter.plugin.platform.PlatformViewsController2; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; @@ -1464,4 +1468,32 @@ public class FlutterViewTest { return View.SYSTEM_UI_FLAG_FULLSCREEN; } } + + /** + * Test that autofill methods do nothing when TextInputPlugin is null. This verifies that no + * NullPointerException is thrown and the plugin methods are not called. + */ + @Test + public void autofill_doesNothingWhenTextInputPluginIsNull() throws Exception { + // Setup: Create FlutterView without attaching to engine + // This simulates the case where textInputPlugin is null (e.g., when + // attachToEngineAutomatically is false) + FlutterView flutterView = new FlutterView(ctx); + + // Verify textInputPlugin is null (not initialized) + Field textInputPluginField = FlutterView.class.getDeclaredField("textInputPlugin"); + textInputPluginField.setAccessible(true); + assertNull(textInputPluginField.get(flutterView)); + + // Test onProvideAutofillVirtualStructure - should not throw NPE + ViewStructure structure = mock(ViewStructure.class); + int flags = 0; + flutterView.onProvideAutofillVirtualStructure(structure, flags); + // No exception should be thrown + + // Test autofill - should not throw NPE + SparseArray values = mock(SparseArray.class); + flutterView.autofill(values); + // No exception should be thrown + } }