Skip to content

Commit 5df98bd

Browse files
fix(core): rebuild nested-frame fragment when its view was destroyed on reattach
Extends the _ensureEntryFragment recovery from #10713 to cover the case where the fragment object survives but its view has been destroyed. Without this, the nested frame renders blank after the parent is briefly covered and dismissed. Also guards entry.fragment = null in onDestroy so the old fragment's teardown doesn't overwrite the new reference assigned by the rebuild path.
1 parent 57b8815 commit 5df98bd

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

packages/core/ui/frame/frame-helper-for-android.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,15 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks
236236
// [nested frames / fragments] see https://github.com/NativeScript/NativeScript/issues/6629
237237
// retaining reference to a destroyed fragment here somehow causes a cryptic
238238
// "IllegalStateException: Failure saving state: active fragment has cleared index: -1"
239-
// in a specific mixed parent / nested frame navigation scenario
240-
entry.fragment = null;
239+
// in a specific mixed parent / nested frame navigation scenario.
240+
//
241+
// Only null out entry.fragment if it still points to THIS fragment. _ensureEntryFragment
242+
// may have already replaced it with a new fragment (when recovering one whose view was
243+
// destroyed) — if THIS old fragment is destroyed afterwards, we must not overwrite the
244+
// newer reference.
245+
if (entry.fragment === fragment) {
246+
entry.fragment = null;
247+
}
241248

242249
const page = entry.resolvedPage;
243250
if (!page) {

packages/core/ui/frame/index.android.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ export class Frame extends FrameBase {
8484
private _cachedTransitionState: TransitionState;
8585
private _frameCreateTimeout: NodeJS.Timeout;
8686

87+
// True while inside an onLoaded()-driven _ensureEntryFragment call. _ensureEntryFragment
88+
// also runs from _onAttachedToWindow during outgoing navigations, where the fragment's
89+
// view is null because it's being torn down on purpose — we must not rebuild it then.
90+
// This flag lets _ensureEntryFragment tell the two cases apart.
91+
private _pendingFragmentCheck = false;
92+
8793
constructor() {
8894
super();
8995
this._android = new AndroidFrame(this);
@@ -245,6 +251,10 @@ export class Frame extends FrameBase {
245251
}
246252

247253
onLoaded(): void {
254+
// Tell _ensureEntryFragment this pass is from onLoaded (an incoming reattach)
255+
// rather than from _onAttachedToWindow during an outgoing transition. See the
256+
// field declaration of _pendingFragmentCheck for why this matters.
257+
this._pendingFragmentCheck = true;
248258
if (this._originalBackground) {
249259
this.backgroundColor = null;
250260
this.backgroundColor = this._originalBackground;
@@ -274,6 +284,7 @@ export class Frame extends FrameBase {
274284
// though we should not do it on app "start"
275285
// or it will create a "flash" to activity background color
276286
if (this._isReset && !this._attachedToWindow) {
287+
this._pendingFragmentCheck = false;
277288
return;
278289
}
279290

@@ -282,7 +293,13 @@ export class Frame extends FrameBase {
282293
// so we manually check on loaded event if the fragment is not recreated and recreate it
283294
const currentEntry = this._currentEntry || this._executingContext?.entry;
284295
if (currentEntry) {
285-
if (!currentEntry.fragment) {
296+
// Rebuild if either:
297+
// (a) there is no fragment at all, or
298+
// (b) the fragment exists but its view was destroyed AND we got here via
299+
// onLoaded (not via _onAttachedToWindow during an outgoing transition,
300+
// where a null view is expected and a rebuild would be wrong).
301+
const fragmentNeedsRebuild = !currentEntry.fragment || (currentEntry.fragment.getView() == null && this._pendingFragmentCheck);
302+
if (fragmentNeedsRebuild) {
286303
const manager = this._getFragmentManager();
287304
const transaction = manager.beginTransaction();
288305
currentEntry.fragment = this.createFragment(currentEntry, currentEntry.fragmentTag);
@@ -292,6 +309,7 @@ export class Frame extends FrameBase {
292309
}
293310
}
294311

312+
this._pendingFragmentCheck = false;
295313
this._frameCreateTimeout = null;
296314
}, 0);
297315
}

0 commit comments

Comments
 (0)