11#nullable enable
22using System ;
33using System . Collections . Generic ;
4- using System . Threading . Tasks ;
54using Android . OS ;
65using Android . Views ;
76using AndroidX . CoordinatorLayout . Widget ;
@@ -298,10 +297,15 @@ internal void SwitchToShellItem(ShellItem newItem)
298297 // That causes SetSelectedTab on the old BNV, firing OnNavigationItemSelected which
299298 // poisons the old ShellItem's CurrentItem via ProposeSection.
300299 _switchingShellItem = true ;
301- SetVirtualView ( newItem ) ;
302- _switchingShellItem = false ;
303-
304- _preserveFragmentResources = false ;
300+ try
301+ {
302+ SetVirtualView ( newItem ) ;
303+ }
304+ finally
305+ {
306+ _switchingShellItem = false ;
307+ _preserveFragmentResources = false ;
308+ }
305309
306310 // Rebuild ViewPager2 adapter for new ShellItem's sections
307311 SetupViewPagerAdapter ( ) ;
@@ -638,37 +642,15 @@ void NotifyTopTabsForSectionSwitch(ShellSection? oldSection, ShellSection? newSe
638642 }
639643
640644 /// <summary>
641- /// Handles the back button press. Returns true if navigation was handled, false otherwise.
642- /// Back navigation is delegated to the current section's StackNavigationManager.
645+ /// Handles the back button press by delegating to Shell's full navigation pipeline.
646+ /// Shell.SendBackButtonPressed() handles BackButtonBehavior commands, page overrides,
647+ /// navigation stack pops, modal dismissal, and ShellNavigatingEventArgs cancellation.
648+ /// Returns true if Shell handled it, false if system should handle (app exit).
643649 /// </summary>
644650 internal bool OnBackButtonPressed ( )
645651 {
646- if ( _shellSection is null )
647- {
648- return false ;
649- }
650-
651- var stack = _shellSection . Stack ;
652-
653- // If we're at the root page, don't handle back - let the system handle it
654- if ( stack . Count <= 1 )
655- {
656- return false ;
657- }
658-
659- // We have pages in the stack, so we can pop
660- Task . Run ( async ( ) =>
661- {
662- try
663- {
664- await _shellSection . Navigation . PopAsync ( ) ;
665- }
666- catch ( Exception )
667- {
668- }
669- } ) ;
670-
671- return true ; // We handled the back press
652+ var shell = VirtualView ? . FindParentOfType < Shell > ( ) ;
653+ return shell ? . SendBackButtonPressed ( ) ?? false ;
672654 }
673655
674656 #endregion Navigation Support
@@ -1156,10 +1138,17 @@ public void Dispose()
11561138 /// </summary>
11571139 class ShellItemWrapperFragment : Fragment
11581140 {
1159- readonly ShellItemHandler _handler ;
1141+ readonly ShellItemHandler ? _handler ;
11601142 CoordinatorLayout ? _rootLayout ;
11611143 ShellBackPressedCallback ? _backPressedCallback ;
11621144
1145+ // Default constructor required by Android's FragmentManager for fragment restoration.
1146+ // Without this, FragmentManager.instantiate() crashes on process-death restoration.
1147+ public ShellItemWrapperFragment ( )
1148+ {
1149+ _handler = null ;
1150+ }
1151+
11631152 public ShellItemWrapperFragment ( ShellItemHandler handler )
11641153 {
11651154 _handler = handler ;
@@ -1169,6 +1158,13 @@ public ShellItemWrapperFragment(ShellItemHandler handler)
11691158
11701159 public override AView OnCreateView ( LayoutInflater inflater , ViewGroup ? container , Bundle ? savedInstanceState )
11711160 {
1161+ // If restored without proper handler reference, return empty view.
1162+ // The Shell infrastructure will recreate proper fragments after reconnecting.
1163+ if ( _handler is null )
1164+ {
1165+ return new global ::Android . Widget . FrameLayout ( inflater . Context ! ) ;
1166+ }
1167+
11721168 // Inflate from XML layout — consistent with NavigationViewHandler/FlyoutViewHandler pattern
11731169 var rootView = inflater . Inflate ( Resource . Layout . shellitemlayout , container , false )
11741170 ?? throw new InvalidOperationException ( "shellitemlayout inflation failed" ) ;
@@ -1195,8 +1191,14 @@ public override void OnViewCreated(AView view, Bundle? savedInstanceState)
11951191 {
11961192 base . OnViewCreated ( view , savedInstanceState ) ;
11971193
1194+ // Skip setup if restored without handler (parameterless constructor path)
1195+ if ( _handler is null )
1196+ {
1197+ return ;
1198+ }
1199+
11981200 // Setup back button handling
1199- _backPressedCallback = new ShellBackPressedCallback ( _handler ) ;
1201+ _backPressedCallback = new ShellBackPressedCallback ( _handler , this ) ;
12001202 RequireActivity ( ) . OnBackPressedDispatcher . AddCallback ( ViewLifecycleOwner , _backPressedCallback ) ;
12011203
12021204 // Setup the shared toolbar
@@ -1255,20 +1257,33 @@ protected override void Dispose(bool disposing)
12551257 sealed class ShellBackPressedCallback : AndroidX . Activity . OnBackPressedCallback
12561258 {
12571259 readonly ShellItemHandler _handler ;
1260+ readonly Fragment _fragment ;
12581261
1259- public ShellBackPressedCallback ( ShellItemHandler handler ) : base ( true )
1262+ public ShellBackPressedCallback ( ShellItemHandler handler , Fragment fragment ) : base ( true )
12601263 {
12611264 _handler = handler ;
1265+ _fragment = fragment ;
12621266 }
12631267
12641268 public override void HandleOnBackPressed ( )
12651269 {
1266- // Let the handler try to handle the back press
1270+ // Route through Shell's full back navigation pipeline.
1271+ // Shell.SendBackButtonPressed() handles:
1272+ // - BackButtonBehavior.Command execution
1273+ // - Page.OnBackButtonPressed() overrides
1274+ // - Navigation stack pops (via Shell.OnBackButtonPressed)
1275+ // - Modal stack dismissal
1276+ // - ShellNavigatingEventArgs cancellation
1277+ // This matches the old renderer behavior where the lifecycle chain
1278+ // (Activity → Window → Shell) naturally invoked the full pipeline.
12671279 if ( ! _handler . OnBackButtonPressed ( ) )
12681280 {
1269- // Handler didn't handle it (we're at root), let system handle it
1281+ // Shell didn't handle it (at root, no stack to pop, not cancelled).
1282+ // Forward to system by temporarily disabling this callback so the
1283+ // dispatcher falls through to the next handler in the chain
1284+ // (e.g., the Activity's default that finishes the app).
12701285 this . Enabled = false ;
1271- // The system will handle app exit
1286+ _fragment . RequireActivity ( ) . OnBackPressedDispatcher . OnBackPressed ( ) ;
12721287 this . Enabled = true ;
12731288 }
12741289 }
@@ -1389,7 +1404,7 @@ public override Fragment CreateFragment(int position)
13891404 return observableFragment . Fragment ;
13901405 }
13911406
1392- throw new InvalidOperationException ( $ "ShellSectionRenderer for { section . Title } is not an IShellObservableFragment") ;
1407+ throw new InvalidOperationException ( $ "ShellSectionHandler for { section . Title } is not an IShellObservableFragment") ;
13931408 }
13941409
13951410 public IShellSectionRenderer ? GetRenderer ( int position )
0 commit comments