Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

[Android] androidOverflowInset listener is lost after the native view is recreated

Abierto Apto para principiantes
#11,460 2 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
78/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
android, typescript
Área
mobile-dev

Línea de trabajo

Comienza en packages/core/ui/core/view/index.android.ts leyendo initNativeView(), disposeNativeView() y la gestión del estado del listener de insets. Reproduce el problema con apps/toolbox en Android alternando la gestión manual de insets y cambiando entre la navegación por gestos y la navegación de 3 botones. Se considera terminado cuando el handler androidOverflowInset se activa para la vista nativa recreada e informa del inset inferior actual después de cada cambio del modo de navegación.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

bug-pending-triage
Issue Description

On Android, once a layout's native view is recreated, its androidOverflowInset handler never fires again. This is a runtime issue, and nothing is logged.

This affects apps that handle insets themselves: a Page or layout with androidOverflowEdge="dont-apply" and an androidOverflowInset handler that applies the insets. After the activity is recreated, for example after switching between gesture and 3-button navigation, the handler is not called anymore and the page keeps the insets it had before. In 3-button mode the bottom inset stays at the gesture bar height, so content ends up under the navigation buttons. It stays that way until the app is restarted.

Expected: the handler fires again for the new native view and receives the current insets.

Cause. View.disposeNativeView() in packages/core/ui/core/view/index.android.ts resets the touch and layout-change listener flags but not insetListenerIsSet. For the new native view, initNativeView() sees insetListenerIsSet === true and needsInsetListener === false, so it skips setInsetListener(). The new LayoutBase keeps insetListener = null, and in the OverflowEdgeDontApply branch nothing calls the handler.

Proposed fix: reset the flag in disposeNativeView() so initNativeView() attaches the listener to the new native view:

--- a/packages/core/ui/core/view/index.android.ts
+++ b/packages/core/ui/core/view/index.android.ts
@@ -860,6 +860,14 @@ export class View extends ViewCommon {
 				this.layoutChangeListener = null;
 			}
 		}
+		if (this.insetListenerIsSet) {
+			// Mark the listener as needed so initNativeView() attaches it to the new native view.
+			this.insetListenerIsSet = false;
+			this.needsInsetListener = true;
+			if (this.nativeViewProtected && (this.nativeViewProtected as any).setInsetListener) {
+				(this.nativeViewProtected as any).setInsetListener(null);
+			}
+		}
 		super.disposeNativeView();
 	}

Alternative, also verified with the same reproduction: attach the listener in initNativeView() when there are subscribers, the same way as the layout-change listener. This makes needsInsetListener unnecessary:

--- a/packages/core/ui/core/view/index.android.ts
+++ b/packages/core/ui/core/view/index.android.ts
@@ -601,7 +601,6 @@ export class View extends ViewCommon {
 	private layoutChangeListener: android.view.View.OnLayoutChangeListener;
 	private _rootManager: androidx.fragment.app.FragmentManager;
 	private insetListenerIsSet: boolean;
-	private needsInsetListener: boolean;
 
 	nativeViewProtected: android.view.View;
 
@@ -670,9 +669,6 @@ export class View extends ViewCommon {
 				);
 				this.insetListenerIsSet = true;
 			}
-			this.needsInsetListener = false;
-		} else {
-			this.needsInsetListener = true;
 		}
 	}
 
@@ -837,7 +833,7 @@ export class View extends ViewCommon {
 			this.setOnLayoutChangeListener();
 		}
 
-		if (!this.insetListenerIsSet && this.needsInsetListener) {
+		if (!this.insetListenerIsSet && this.hasListeners(ViewCommon.androidOverflowInsetEvent)) {
 			this.setInsetListener();
 		}
 	}
@@ -860,6 +856,12 @@ export class View extends ViewCommon {
 				this.layoutChangeListener = null;
 			}
 		}
+		if (this.insetListenerIsSet) {
+			this.insetListenerIsSet = false;
+			if (this.nativeViewProtected && (this.nativeViewProtected as any).setInsetListener) {
+				(this.nativeViewProtected as any).setInsetListener(null);
+			}
+		}
 		super.disposeNativeView();
 	}

Both versions behave the same when the handler is added at runtime and when it is set in XML, before the native view exists. The first one is what I ship in production in my app. I'm happy to use either in the PR.

Verified that #11434 fixes this: with it applied, the same reproduction shows a new event with bottom 144px after switching to 3-button navigation and bottom 72px after switching back.

Reproduction

The apps/toolbox ete page already has manual inset handling, but its handler never updated the padding binding: it assigned this.padding before calling this.set('padding', this.padding), so set() saw no change. The diff below fixes that and shows each androidOverflowInset event under the buttons:

--- a/apps/toolbox/src/pages/ete.ts
+++ b/apps/toolbox/src/pages/ete.ts
@@ -47,6 +47,8 @@ export class ETEData extends Observable {
 	}
 
 	padding = '0';
+	insetEvents = 0;
+	insetInfo = 'androidOverflowInset: no events';
 
 	inset = {
 		top: 0,
@@ -67,8 +69,11 @@ export class ETEData extends Observable {
 		this.inset.left = args.inset.left;
 		this.inset.right = args.inset.right;
 		this.inset.bottom = args.inset.bottom;
-		this.padding = `${this.inset.top}px ${this.inset.right}px ${this.inset.bottom}px ${this.inset.left}px`;
-		this.set('padding', this.padding);
+		this.set('insetEvents', this.insetEvents + 1);
+		this.set('insetInfo', `androidOverflowInset #${this.insetEvents}: top ${this.inset.top}px, bottom ${this.inset.bottom}px`);
+		if (this.overflowManual) {
+			this.set('padding', `${this.inset.top}px ${this.inset.right}px ${this.inset.bottom}px ${this.inset.left}px`);
+		}
 	}
 
 	toggleManualInsets(args) {
@@ -77,7 +82,9 @@ export class ETEData extends Observable {
 		this.overflowManual = !this.overflowManual;
 		page.off(View.androidOverflowInsetEvent, this.onInset, this);
 		page.on(View.androidOverflowInsetEvent, this.onInset, this);
-		this.overflowManual ? (this.padding = '0') : (this.padding = `${this.inset.top}px ${this.inset.right}px ${this.inset.bottom}px ${this.inset.left}px`);
+		if (!this.overflowManual) {
+			this.set('padding', '0');
+		}
 		page.androidOverflowEdge = this.overflowManual ? 'dont-apply' : 'none';
 	}
 }
--- a/apps/toolbox/src/pages/ete.xml
+++ b/apps/toolbox/src/pages/ete.xml
@@ -10,6 +10,7 @@
        <Button tap="{{randomColor}}" text="Update BG to a random color" />
        <Button tap="{{toggleOverflowTopBottom}}" text="Toggle Overflow Top and Bottom" />
        <Button tap="{{toggleManualInsets}}" text="Toggle manual overflow handling" />
+       <Label text="{{ insetInfo }}" textWrap="true" textAlignment="center" padding="16" />
       </StackLayout>
     </ScrollView>
 </Page>
  1. Apply the toolbox diff above and run apps/toolbox on Android with gesture navigation enabled.
  2. Open ete and tap Toggle manual overflow handling. The label shows androidOverflowInset #1: top 156px, bottom 72px.
  3. Switch to 3-button navigation (Settings → System → Navigation mode, or adb shell cmd overlay enable-exclusive --category com.android.internal.systemui.navbar.threebutton). This recreates the activity.
  4. The label still shows #1 … bottom 72px: the handler is not called, although the navigation bar is now taller.
  5. Switch back to gesture navigation (adb shell cmd overlay enable-exclusive --category com.android.internal.systemui.navbar.gestural). The label still shows #1.

With either fix applied, step 4 shows a new event with bottom 144px, and step 5 shows another one with bottom 72px.

Before the fix Image
After the fix Image
Relevant log output (if applicable)

Environment

Reproduced in apps/toolbox on main (a032c224c), Android 16 (API 36) emulator, sdk_gphone64_arm64.
Also reproduced in a production app on @nativescript/core 9.0.18 on a physical device.

OS: macOS 15.7.3
CPU: (10) arm64 Apple M1 Pro
Shell: /opt/homebrew/bin/fish
node: 26.10.0
npm: 11.19.1
nativescript: 9.1.1

# android
java: 17.0.20.1
ndk: Not Found
apis: 33, 34, 35, 36, 36
build_tools: 33.0.1, 34.0.0, 35.0.0, 35.0.1, 36.0.0, 36.1.0
system_images:
  - android-35 | Google Play ARM 64 v8a
  - android-36 | Google Play ARM 64 v8a

# ios
xcode: 26.3/17C529
cocoapods: 1.16.2
python: Not Found
python3: 3.9.6
ruby: 3.3.12
platforms:
  - DriverKit 25.2
  - iOS 26.2
  - macOS 26.2
  - tvOS 26.2
  - visionOS 26.2
  - watchOS 26.2
Dependencies
"dependencies": {
  "@valor/nativescript-websockets": "^2.0.3",
  "nativescript-theme-core": "^1.0.4"
},
"devDependencies": {
  "@analogjs/vite-plugin-angular": "2.1.3",
  "@angular/build": "^21.0.0",
  "@angular/compiler-cli": "^21.0.0",
  "@csstools/css-calc": "~2.1.2",
  "@csstools/css-color-parser": "^3.0.8",
  "@csstools/css-parser-algorithms": "^3.0.4",
  "@csstools/css-tokenizer": "^3.0.3",
  "@nativescript/hook": "^3.0.4",
  "@nativescript/nx": "^22.0.0",
  "@nstudio/focus": "^20.0.2",
  "@nstudio/nps-i": "~2.0.0",
  "@nx/devkit": "22.5.4",
  "@nx/eslint-plugin": "22.5.4",
  "@nx/jest": "22.5.4",
  "@nx/js": "22.5.4",
  "@nx/node": "22.5.4",
  "@nx/plugin": "22.5.4",
  "@nx/vite": "22.5.4",
  "@nx/vitest": "22.5.4",
  "@nx/web": "22.5.4",
  "@nx/workspace": "22.5.4",
  "@prettier/plugin-xml": "^3.4.1",
  "@rollup/plugin-alias": "^6.0.0",
  "@rollup/plugin-commonjs": "^29.0.0",
  "@rollup/plugin-replace": "^6.0.3",
  "@swc-node/register": "1.11.1",
  "@swc/core": "1.15.8",
  "@swc/helpers": "0.5.19",
  "@types/jest": "30.0.0",
  "@types/node": "^20.0.0",
  "@types/ws": "^8.18.1",
  "@typescript-eslint/eslint-plugin": "^8.46.4",
  "@typescript-eslint/parser": "^8.46.4",
  "@vitejs/plugin-vue": "^6.0.5",
  "@vitejs/plugin-vue-jsx": "^5.1.5",
  "@vitest/coverage-v8": "4.0.9",
  "@vitest/ui": "4.0.9",
  "@vue/compiler-sfc": "^3.5.24",
  "acorn": "^8.15.0",
  "acorn-stage3": "^4.0.0",
  "copy-webpack-plugin": "^13.0.0",
  "copyfiles": "^2.4.0",
  "css": "^3.0.0",
  "css-tree": "^3.1.0",
  "css-what": "^6.1.0",
  "dotenv": "~16.4.0",
  "dotenv-webpack": "^7.0.0",
  "emoji-regex": "^10.3.0",
  "enhanced-resolve": "^5.18.3",
  "esbuild": "^0.27.4",
  "eslint": "~8.57.0",
  "eslint-config-prettier": "^10.0.0",
  "fork-ts-checker-webpack-plugin": "^7.0.0",
  "form-data": ">=4.0.4",
  "gonzales": "^1.0.7",
  "husky": "^9.0.0",
  "jest": "30.0.5",
  "jest-environment-jsdom": "30.0.5",
  "jest-util": "30.0.5",
  "jiti": "2.4.2",
  "jsdom": "~22.1.0",
  "lint-staged": "^15.2.0",
  "loader-utils": "^2.0.0 || ^3.0.0",
  "module-alias": "^2.2.2",
  "nativescript": "9.1.0-alpha.17",
  "nativescript-typedoc-theme": "1.1.0",
  "nx": "22.5.4",
  "parse-css": "git+https://github.com/tabatkins/parse-css.git",
  "parserlib": "^1.1.1",
  "plist": "^5.0.0",
  "postcss": "^8.0.0",
  "postcss-import": "^16.0.0",
  "postcss-loader": "^8.0.0",
  "prettier": "^3.2.5",
  "react-reconciler": "^0.33.0",
  "sass": "^1.72.0",
  "sass-loader": "^16.0.0",
  "shady-css-parser": "^0.1.0",
  "terser-webpack-plugin": "^5.0.0",
  "tree-kill": "^1.2.2",
  "ts-dedent": "^2.2.0",
  "ts-jest": "29.4.5",
  "ts-loader": "^9.0.0",
  "ts-node": "10.9.2",
  "ts-patch": "^3.0.0",
  "tslib": "^2.6.0",
  "typedoc": "^0.28.14",
  "typescript": "5.9.3",
  "vite": "^8.0.0",
  "vite-plugin-solid": "^2.11.11",
  "vite-plugin-static-copy": "^4.1.1",
  "vitest": "4.0.9",
  "vue-loader": "^15.0.0 <= 15.9.8",
  "vue-tsc": "^3.2.5",
  "webpack-bundle-analyzer": "^4.0.0",
  "webpack-chain": "^6.0.0",
  "webpack-merge": "^6.0.0",
  "webpack-virtual-modules": "^0.4.0",
  "zx": "^8.3.0"
}
Please accept these terms
Lenguaje dominante
TypeScript
Estrellas
25.7k
Forks
1.7k
Merge medio
2 d 19 h
PR fusionados (30 d)
27

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de NativeScript/NativeScript

Todos los issues de NativeScript/NativeScript

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.