{"slug":"maui-app-lifecycle","title":"maui-app-lifecycle","summary":".NET MAUI app lifecycle guidance — the four app states, cross-platform Window lifecycle events (Created, Activated, Deactivated, Stopped, Resumed, Destroying), platform-specific lifecycle mapping, backgrounding and resume behavior, and state-preservation patterns. USE FOR: \"app l","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-24T05:37:29.989775Z","repo":{"url":"https://github.com/dotnet/skills","stars":5471,"forks":418,"license":"MIT","updatedAt":"2026-09-24T06:38:55Z"},"bodyHtml":"<hr>\n<h2>name: maui-app-lifecycle\ndescription: &gt;-\n.NET MAUI app lifecycle guidance — the four app states, cross-platform Window\nlifecycle events (Created, Activated, Deactivated, Stopped, Resumed, Destroying),\nplatform-specific lifecycle mapping, backgrounding and resume behavior, and\nstate-preservation patterns.\nUSE FOR: \"app lifecycle\", \"window lifecycle events\", \"save state on background\",\n\"resume app\", \"OnStopped\", \"OnResumed\", \"backgrounding\", \"deactivated event\",\n\"ConfigureLifecycleEvents\", \"platform lifecycle hooks\".\nDO NOT USE FOR: navigation events (use maui-shell-navigation),\ndependency injection setup (use maui-dependency-injection),\nplatform API invocation (use conditional compilation and partial classes).\nlicense: MIT</h2>\n<h1>.NET MAUI App Lifecycle</h1>\n<p>Handle application state transitions correctly in .NET MAUI. This skill covers the cross-platform Window lifecycle events, their platform-native mappings, and patterns for preserving state across backgrounding and resume cycles.</p>\n<h2>When to Use</h2>\n<ul>\n<li>Saving or restoring state when the app backgrounds or resumes</li>\n<li>Subscribing to Window lifecycle events (Created, Activated, Deactivated, Stopped, Resumed, Destroying)</li>\n<li>Hooking into platform-native lifecycle callbacks via <code>ConfigureLifecycleEvents</code></li>\n<li>Deciding where to place initialization, teardown, or refresh logic</li>\n<li>Understanding the difference between Deactivated and Stopped</li>\n</ul>\n<h2>When Not to Use</h2>\n<ul>\n<li>Page-level navigation events — use Shell navigation guidance instead</li>\n<li>Registering services at startup — use dependency injection guidance instead</li>\n<li>Calling platform-specific APIs outside lifecycle context — use platform invoke guidance instead</li>\n</ul>\n<h2>Inputs</h2>\n<ul>\n<li>The target lifecycle transition (e.g., \"save draft when backgrounded\", \"refresh data on resume\")</li>\n<li>Which platforms the developer targets (Android, iOS, Mac Catalyst, Windows)</li>\n<li>Whether the app uses multiple windows (iPad, Mac Catalyst, desktop Windows)</li>\n</ul>\n<h2>App States</h2>\n<p>A .NET MAUI app moves through four states:</p>\n<table>\n<thead>\n<tr>\n<th>State</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Not Running</strong></td>\n<td>Process does not exist</td>\n</tr>\n<tr>\n<td><strong>Running</strong></td>\n<td>Foreground, receiving input</td>\n</tr>\n<tr>\n<td><strong>Deactivated</strong></td>\n<td>Visible but lost focus (dialog, split-screen, notification shade)</td>\n</tr>\n<tr>\n<td><strong>Stopped</strong></td>\n<td>Fully backgrounded, UI not visible</td>\n</tr>\n</tbody>\n</table>\n<p>Typical flow: Not Running → Running → Deactivated → Stopped → Running (resumed) or Not Running (terminated).</p>\n<h2>Window Lifecycle Events</h2>\n<p><code>Microsoft.Maui.Controls.Window</code> exposes six cross-platform events:</p>\n<table>\n<thead>\n<tr>\n<th>Event</th>\n<th>Fires when</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>Created</code></td>\n<td>Native window allocated</td>\n</tr>\n<tr>\n<td><code>Activated</code></td>\n<td>Window receives input focus</td>\n</tr>\n<tr>\n<td><code>Deactivated</code></td>\n<td>Window loses focus (may still be visible)</td>\n</tr>\n<tr>\n<td><code>Stopped</code></td>\n<td>Window is no longer visible</td>\n</tr>\n<tr>\n<td><code>Resumed</code></td>\n<td>Window returns to foreground after Stopped</td>\n</tr>\n<tr>\n<td><code>Destroying</code></td>\n<td>Native window is being torn down</td>\n</tr>\n</tbody>\n</table>\n<h3>Subscribing via CreateWindow</h3>\n<p>Override <code>CreateWindow</code> in your <code>App</code> class and attach event handlers:</p>\n<pre><code>public partial class App : Application\n{\n    protected override Window CreateWindow(IActivationState? activationState)\n    {\n        var window = base.CreateWindow(activationState);\n\n        window.Created += (s, e) =&gt; Debug.WriteLine(\"Created\");\n        window.Activated += (s, e) =&gt; Debug.WriteLine(\"Activated\");\n        window.Deactivated += (s, e) =&gt; Debug.WriteLine(\"Deactivated\");\n        window.Stopped += (s, e) =&gt; Debug.WriteLine(\"Stopped\");\n        window.Resumed += (s, e) =&gt; Debug.WriteLine(\"Resumed\");\n        window.Destroying += (s, e) =&gt; Debug.WriteLine(\"Destroying\");\n\n        return window;\n    }\n}\n</code></pre>\n<h3>Subscribing via a Custom Window Subclass</h3>\n<p>Create a <code>Window</code> subclass and override the virtual methods:</p>\n<pre><code>public class AppWindow : Window\n{\n    public AppWindow(Page page) : base(page) { }\n\n    protected override void OnActivated() { /* refresh UI */ }\n    protected override void OnStopped() { /* save state */ }\n    protected override void OnResumed() { /* restore state */ }\n    protected override void OnDestroying() { /* cleanup */ }\n}\n</code></pre>\n<p>Return it from <code>CreateWindow</code>:</p>\n<pre><code>protected override Window CreateWindow(IActivationState? activationState)\n    =&gt; new AppWindow(new AppShell());\n</code></pre>\n<h2>Workflow: Save and Restore State on Background</h2>\n<ol>\n<li><strong>Identify transient state</strong> — draft text, scroll position, form inputs, timer values.</li>\n<li><strong>Save in <code>OnStopped</code></strong> — use <code>Preferences</code> for small values or file serialization for larger state.</li>\n<li><strong>Restore in <code>OnResumed</code></strong> — read back saved values and apply to your view model.</li>\n<li><strong>Also save in <code>OnDestroying</code></strong> on Android — the back button can skip <code>Stopped</code> entirely.</li>\n<li><strong>Keep handlers fast</strong> — complete within 1–2 seconds to avoid ANR on Android or watchdog kills on iOS.</li>\n</ol>\n<pre><code>protected override void OnStopped()\n{\n    base.OnStopped();\n    Preferences.Set(\"draft_text\", _viewModel.DraftText);\n    Preferences.Set(\"scroll_y\", _viewModel.ScrollY);\n}\n\nprotected override void OnResumed()\n{\n    base.OnResumed();\n    _viewModel.DraftText = Preferences.Get(\"draft_text\", string.Empty);\n    _viewModel.ScrollY = Preferences.Get(\"scroll_y\", 0.0);\n}\n\nprotected override void OnDestroying()\n{\n    base.OnDestroying();\n    // Android back-button can skip Stopped\n    Preferences.Set(\"draft_text\", _viewModel.DraftText);\n}\n</code></pre>\n<h2>Platform Lifecycle Mapping</h2>\n<h3>Android</h3>\n<table>\n<thead>\n<tr>\n<th>Window Event</th>\n<th>Android Callback</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Created</td>\n<td><code>OnCreate</code></td>\n</tr>\n<tr>\n<td>Activated</td>\n<td><code>OnResume</code></td>\n</tr>\n<tr>\n<td>Deactivated</td>\n<td><code>OnPause</code></td>\n</tr>\n<tr>\n<td>Stopped</td>\n<td><code>OnStop</code></td>\n</tr>\n<tr>\n<td>Resumed</td>\n<td><code>OnRestart</code> → <code>OnStart</code> → <code>OnResume</code></td>\n</tr>\n<tr>\n<td>Destroying</td>\n<td><code>OnDestroy</code></td>\n</tr>\n</tbody>\n</table>\n<h3>iOS / Mac Catalyst</h3>\n<table>\n<thead>\n<tr>\n<th>Window Event</th>\n<th>UIKit Callback</th>\n<th><code>AddiOS</code> builder method</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Created</td>\n<td><code>WillFinishLaunching</code> / <code>SceneWillConnect</code></td>\n<td><code>.WillFinishLaunching()</code> / <code>.SceneWillConnect()</code></td>\n</tr>\n<tr>\n<td>Activated</td>\n<td><code>DidBecomeActive</code></td>\n<td><code>.OnActivated()</code></td>\n</tr>\n<tr>\n<td>Deactivated</td>\n<td><code>WillResignActive</code></td>\n<td><code>.OnResignActivation()</code></td>\n</tr>\n<tr>\n<td>Stopped</td>\n<td><code>DidEnterBackground</code></td>\n<td><code>.DidEnterBackground()</code></td>\n</tr>\n<tr>\n<td>Resumed</td>\n<td><code>WillEnterForeground</code></td>\n<td><code>.WillEnterForeground()</code></td>\n</tr>\n<tr>\n<td>Destroying</td>\n<td><code>WillTerminate</code></td>\n<td><code>.WillTerminate()</code></td>\n</tr>\n</tbody>\n</table>\n<blockquote>\n<p>⚠️ The UIKit selector names and the <code>AddiOS</code> builder method names differ for\nactivation. There is <strong>no</strong> <code>.DidBecomeActive()</code> or <code>.WillResignActive()</code> builder\nmethod — use <code>.OnActivated()</code> and <code>.OnResignActivation()</code> or the code will not compile.</p>\n</blockquote>\n<h3>Windows (WinUI)</h3>\n<table>\n<thead>\n<tr>\n<th>Window Event</th>\n<th>WinUI Callback</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Created</td>\n<td><code>OnLaunched</code></td>\n</tr>\n<tr>\n<td>Activated</td>\n<td><code>Activated</code> (foreground)</td>\n</tr>\n<tr>\n<td>Deactivated</td>\n<td><code>Activated</code> (background)</td>\n</tr>\n<tr>\n<td>Stopped</td>\n<td><code>VisibilityChanged</code> (false)</td>\n</tr>\n<tr>\n<td>Resumed</td>\n<td><code>VisibilityChanged</code> (true)</td>\n</tr>\n<tr>\n<td>Destroying</td>\n<td><code>Closed</code></td>\n</tr>\n</tbody>\n</table>\n<h2>Hooking Native Lifecycle Directly</h2>\n<p>Use <code>ConfigureLifecycleEvents</code> in <code>MauiProgram.cs</code> when you need platform-specific callbacks beyond what Window events provide:</p>\n<pre><code>builder.ConfigureLifecycleEvents(events =&gt;\n{\n#if ANDROID\n    events.AddAndroid(android =&gt; android\n        .OnCreate((activity, bundle) =&gt; Debug.WriteLine(\"Android OnCreate\"))\n        .OnResume(activity =&gt; Debug.WriteLine(\"Android OnResume\"))\n        .OnPause(activity =&gt; Debug.WriteLine(\"Android OnPause\"))\n        .OnStop(activity =&gt; Debug.WriteLine(\"Android OnStop\"))\n        .OnDestroy(activity =&gt; Debug.WriteLine(\"Android OnDestroy\")));\n#elif IOS || MACCATALYST\n    events.AddiOS(ios =&gt; ios\n        .OnActivated(app =&gt; Debug.WriteLine(\"iOS OnActivated\"))\n        .OnResignActivation(app =&gt; Debug.WriteLine(\"iOS OnResignActivation\"))\n        .DidEnterBackground(app =&gt; Debug.WriteLine(\"iOS DidEnterBackground\"))\n        .WillEnterForeground(app =&gt; Debug.WriteLine(\"iOS WillEnterForeground\")));\n#elif WINDOWS\n    events.AddWindows(windows =&gt; windows\n        .OnLaunched((app, args) =&gt; Debug.WriteLine(\"Windows OnLaunched\"))\n        .OnActivated((window, args) =&gt; Debug.WriteLine(\"Windows Activated\"))\n        .OnClosed((window, args) =&gt; Debug.WriteLine(\"Windows Closed\")));\n#endif\n});\n</code></pre>\n<h2>Common Pitfalls</h2>\n<ol>\n<li><p><strong>Resumed does not fire on first launch.</strong> The initial sequence is <code>Created</code> → <code>Activated</code>. Use <code>OnActivated</code> for logic that must run on every foreground entry, not <code>OnResumed</code>.</p>\n</li>\n<li><p><strong>Deactivated ≠ Stopped.</strong> A dialog, split-screen, or notification pull-down triggers <code>Deactivated</code> without <code>Stopped</code>. Do not perform heavy saves in <code>OnDeactivated</code> — the app may never actually background.</p>\n</li>\n<li><p><strong>Android back button skips Stopped.</strong> On Android, pressing back may call <code>Destroying</code> directly without <code>Stopped</code>. Place critical save logic in both <code>OnStopped</code> and <code>OnDestroying</code>.</p>\n</li>\n<li><p><strong>Multi-window apps fire events independently.</strong> On iPad, Mac Catalyst, and desktop Windows each <code>Window</code> instance fires its own lifecycle events. Do not assume a single global lifecycle.</p>\n</li>\n<li><p><strong>Long-running handlers cause kills.</strong> Android enforces a ~5 second ANR timeout; iOS has limited background execution time. Keep lifecycle handlers synchronous and fast — use <code>Preferences</code> for quick saves, not database writes.</p>\n</li>\n<li><p><strong>Do not use legacy Xamarin.Forms lifecycle methods.</strong> <code>Application.OnStart()</code>, <code>Application.OnSleep()</code>, and <code>Application.OnResume()</code> exist for backward compatibility but bypass Window-level events. In .NET MAUI, prefer <code>Window</code> lifecycle events (<code>OnActivated</code>, <code>OnStopped</code>, <code>OnResumed</code>, etc.) for correct multi-window behavior.</p>\n</li>\n</ol>\n","files":[{"path":"references/lifecycle-api.md","sizeBytes":5673,"isText":true},{"path":"SKILL.md","sizeBytes":9241,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-24T05:40:48.878081Z","sha256":"F63FEB80B75B5919ADD4470943136BA3557B17F02B7F99597A3B1F22F562B356","sizeBytes":5426},"review":null,"source":{"repositoryUrl":"https://github.com/dotnet/skills","path":"plugins/dotnet-maui/skills/maui-app-lifecycle","license":"MIT","commit":"e115891bd2ac3c7eefd5e30a405f7b5638f5e429","subtreeSha":"DFC3007FAACC6BBD9255AF3A1B3B10158639D5870B0EE4E609BE35C248FD4E1E","lastSyncedAt":"2026-09-24T06:48:49.987562Z"},"reviewedAt":"2026-08-24T05:50:03.414608Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/dotnet/skills/tree/main/plugins/dotnet-maui/skills/maui-app-lifecycle"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install dotnet-skills@llmmart"},{"target":"git","command":"git clone https://github.com/dotnet/skills.git"}]}