09 mei 2025·7 min leestijd

SwiftUI NavigationStack-patronen voor voorspelbare meerstapsflows

SwiftUI NavigationStack-patronen voor meerstapsflows, met heldere routing, veilig Terug-gedrag en praktische voorbeelden voor onboarding en goedkeuringswizards.

SwiftUI NavigationStack-patronen voor voorspelbare meerstapsflows

Wat er misgaat in meerstapsflows

Een meerstapsflow is elke volgorde waarin stap 1 eerst moet gebeuren voordat stap 2 zinvol is. Veelvoorkomende voorbeelden zijn onboarding, een goedkeuringsverzoek (review, bevestigen, verzenden) en wizard-achtige gegevensinvoer waarbij iemand een concept bouwt over meerdere schermen.

Deze flows voelen eenvoudig aan alleen wanneer Terug zich gedraagt zoals gebruikers verwachten. Als Terug hen ergens verrast naartoe brengt, verliezen gebruikers vertrouwen in de app. Dat leidt tot verkeerde inzendingen, geannuleerde onboarding en supporttickets zoals "Ik kan niet terug naar het scherm waar ik was."

Rommelige navigatie ziet er meestal zo uit:

  • De app springt naar het verkeerde scherm, of verlaat de flow te vroeg.
  • Hetzelfde scherm verschijnt twee keer omdat het twee keer gepusht werd.
  • Een stap reset bij Terug en de gebruiker verliest zijn concept.
  • De gebruiker kan stap 3 bereiken zonder stap 1 te voltooien, wat een ongeldige staat creëert.
  • Na een deep link of app-herstart toont de app het juiste scherm maar met de verkeerde data.

Een nuttig mentaal model: een meerstapsflow zijn twee dingen die samen bewegen.

Eerst, een stack van schermen (waar de gebruiker doorheen kan teruggaan). Ten tweede, gedeelde flow-state (conceptdata en voortgang die niet mag verdwijnen alleen omdat een scherm verdwijnt).

Veel NavigationStack-opzetten vallen uit elkaar wanneer de schermstack en de flow-state uit sync raken. Bijvoorbeeld, een onboarding-flow kan “Profiel aanmaken” twee keer pushen (duplicaat-routes), terwijl het conceptprofiel binnen de view leeft en bij herbouw opnieuw wordt gemaakt. De gebruiker drukt op Terug, ziet een andere versie van het formulier en denkt dat de app onbetrouwbaar is.

Voorspelbaar gedrag begint met het benoemen van de flow, definiëren wat Terug op elke stap moet doen en het geven van één duidelijke plek voor flow-state.

Voor meerstapsflows: gebruik NavigationStack in plaats van de oudere NavigationView. NavigationView kan zich anders gedragen tussen iOS-versies en is lastiger te begrijpen bij push, pop of restore van schermen. NavigationStack is de moderne API die navigatie als een echte stack behandelt.

Een NavigationStack slaat een geschiedenis op van waar de gebruiker is geweest. Elke push voegt een bestemming aan de stack toe. Elke terugactie popped één bestemming. Die eenvoudige regel is wat een flow stabiel doet aanvoelen: de UI moet een duidelijke sequentie van stappen weerspiegelen.

Wat de stack echt vasthoudt

SwiftUI slaat je view-objecten niet op. Het slaat de data op die je gebruikte om te navigeren (je routewaarde) en gebruikt die om de bestemming te herbouwen wanneer nodig. Dat heeft een paar praktische gevolgen:

  • Vertrouw er niet op dat een view in leven blijft om belangrijke data te bewaren.
  • Als een scherm state nodig heeft, bewaar die in een model (zoals een ObservableObject) dat buiten de gepushte view leeft.
  • Als je dezelfde bestemming twee keer pusht met verschillende data, behandelt SwiftUI die als verschillende stack-entries.

NavigationPath is wat je gebruikt wanneer je flow niet uit één of twee vaste pushes bestaat. Zie het als een bewerkbare lijst van "waar we naartoe gaan"-waarden. Je kunt routes toevoegen om vooruit te gaan, de laatste route verwijderen om terug te gaan, of het pad volledig vervangen om naar een latere stap te springen.

Het is een goede match wanneer je wizard-achtige stappen nodig hebt, de flow na voltooiing wilt resetten, of een gedeeltelijke flow uit opgeslagen staat wilt herstellen.

Voorspelbaarheid wint van slim: minder verborgen regels (automatische sprongen, impliciete pops, view-gedreven bijwerkingen) betekent later minder vreemde back stack-bugs.

Modelleer de flow met een kleine route-enum

Voorspelbare navigatie begint met één beslissing: houd routing op één plek, en maak elk scherm in de flow een kleine, duidelijke waarde.

Maak één enkele bron van waarheid, bijvoorbeeld een FlowRouter (een ObservableObject) die de NavigationPath beheert. Dat houdt elke push en pop consistent, in plaats van navigatie te verspreiden over meerdere views.

Een eenvoudige router-structuur

Gebruik een enum om stappen te representeren. Voeg alleen associated values toe voor lichtgewicht identifiers (zoals IDs), niet voor hele modellen.

enum Step: Hashable {
    case welcome
    case profile
    case verifyCode(phoneID: UUID)
    case review(applicationID: UUID)
    case done
}

final class FlowRouter: ObservableObject {
    @Published var path = NavigationPath()

    func go(_ step: Step) { path.append(step) }
    func back() { if !path.isEmpty { path.removeLast() } }
    func reset() { path = NavigationPath() }
}

Houd flow-state gescheiden van navigatie-state

Behandel navigatie als “waar de gebruiker is” en flow-state als “wat ze tot nu toe hebben ingevuld.” Plaats flow-data in een eigen store (bijvoorbeeld OnboardingState met naam, e-mail, geüploade documenten) en houd die stabiel terwijl schermen komen en gaan.

Een eenvoudige vuistregel:

  • FlowRouter.path bevat alleen Step-waarden.
  • OnboardingState bevat de invoer en concept-data van de gebruiker.
  • Steps dragen IDs om data op te zoeken, niet de data zelf.

Dit voorkomt fragiele hashing, enorme paths en verrassende resets wanneer SwiftUI views herbouwt.

Stap voor stap: bouw een wizard met NavigationPath

Voor wizard-achtige schermen is de eenvoudigste aanpak dat je de stack zelf bestuurt. Streef naar één bron van waarheid voor “waar ben ik in de flow?” en één manier om vooruit of achteruit te gaan.

Begin met een NavigationStack(path:) gebonden aan een NavigationPath. Elk gepusht scherm wordt gerepresenteerd door een waarde (vaak een enum-case), en je registreert destinations één keer.

import SwiftUI

enum WizardRoute: Hashable {
    case profile
    case verifyEmail
    case permissions
    case review
}

struct OnboardingWizard: View {
    @State private var path = NavigationPath()
    @State private var currentIndex = 0

    private let steps: [WizardRoute] = [.profile, .verifyEmail, .permissions, .review]

    var body: some View {
        NavigationStack(path: $path) {
            StartScreen {
                goToStep(0) // push first step
            }
            .navigationDestination(for: WizardRoute.self) { route in
                switch route {
                case .profile:
                    ProfileStep(onNext: { goToStep(1) })
                case .verifyEmail:
                    VerifyEmailStep(onNext: { goToStep(2) })
                case .permissions:
                    PermissionsStep(onNext: { goToStep(3) })
                case .review:
                    ReviewStep(onEditProfile: { popToStep(0) })
                }
            }
        }
    }

    private func goToStep(_ index: Int) {
        currentIndex = index
        path.append(steps[index])
    }

    private func popToStep(_ index: Int) {
        let toRemove = max(0, currentIndex - index)
        if toRemove > 0 { path.removeLast(toRemove) }
        currentIndex = index
    }
}

Om Terug voorspelbaar te houden, houd je aan een paar gewoontes. Voeg precies één route toe om vooruit te gaan, houd “Volgende” lineair (push alleen de volgende stap), en wanneer je terug moet springen (zoals “Profiel bewerken” vanuit Review), trim de stack naar een bekende index.

Dit voorkomt per ongeluk dubbele schermen en zorgt ervoor dat Terug overeenkomt met wat gebruikers verwachten: één tik is één stap.

Houd data stabiel terwijl schermen komen en gaan

Add essentials to your flow
Use pre-built modules like auth and payments to complete end-to-end flows faster.
Explore Platform

Een meerstapsflow voelt onbetrouwbaar wanneer elk scherm zijn eigen state bezit. Je typt een naam, gaat verder, gaat terug en het veld is leeg omdat de view opnieuw gemaakt werd.

De oplossing is simpel: behandel de flow als één concept-object en laat elke stap het bewerken.

In SwiftUI betekent dat meestal een gedeeld ObservableObject dat één keer bij het begin van de flow wordt aangemaakt en aan elke stap wordt doorgegeven. Bewaar geen conceptwaarden in de @State van elke view tenzij ze echt alleen bij dat scherm horen.

final class OnboardingDraft: ObservableObject {
    @Published var fullName = ""
    @Published var email = ""
    @Published var wantsNotifications = false

    var canGoNextFromProfile: Bool {
        !fullName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
        && email.contains("@")
    }
}

Maak dit object aan bij het toegangspunt en deel het vervolgens met @StateObject en @EnvironmentObject (of geef het expliciet door). Nu kan de stack veranderen zonder data te verliezen.

Bepaal wat een back-navigatie overleeft

Niet alles moet voor altijd blijven. Bepaal je regels van tevoren zodat de flow consistent blijft.

Bewaar gebruikersinvoer (tekstvelden, toggles, selecties) tenzij ze expliciet worden gereset. Reset stap-specifieke UI-state (laadspin, tijdelijke alerts, korte animaties). Maak gevoelige velden leeg (zoals eenmalige codes) bij het verlaten van die stap. Als een keuze latere stappen beïnvloedt, maak dan alleen de afhankelijke velden leeg.

Validatie hoort hier ook. In plaats van gebruikers vooruit te laten gaan en vervolgens een fout op het volgende scherm te tonen, houd ze op de huidige stap totdat die geldig is. Het uitschakelen van de knop op basis van een berekende eigenschap zoals canGoNextFromProfile is vaak voldoende.

Sla checkpoints op zonder te overdrijven

Sommige concepten kunnen alleen in geheugen leven. Andere moeten een app-herstart of crash overleven. Een praktisch standaardbeleid:

  • Bewaar data in geheugen terwijl de gebruiker actief door de stappen beweegt.
  • Persist lokaal bij duidelijke mijlpalen (account aangemaakt, goedkeuring ingediend, betaling gestart).
  • Persist eerder als de flow lang is of gegevensinvoer meer dan een minuut duurt.

Zo kunnen schermen vrij komen en gaan, en voelt de voortgang van de gebruiker stabiel en respectvol voor hun tijd.

Deep links zijn belangrijk omdat echte flows zelden bij stap 1 beginnen. Iemand tikt op een e-mail, pushmelding of gedeelde link en verwacht op het juiste scherm te landen, bijvoorbeeld stap 3 van onboarding of het eindscherm van een goedkeuring.

Met NavigationStack behandel je een deep link als instructies om een geldig pad op te bouwen, niet als een commando om naar één view te teleporteren. Begin bij het begin van de flow en voeg alleen stappen toe die voor deze gebruiker en sessie waar zijn.

Een goed patroon is: parseer de externe ID, laad de minimale data die je nodig hebt en converteer dat naar een reeks routes.

enum Route: Hashable {
    case start
    case profile
    case verifyEmail
    case approve(requestID: String)
}

func pathForDeepLink(requestID: String, hasProfile: Bool, emailVerified: Bool) -> [Route] {
    var routes: [Route] = [.start]
    if !hasProfile { routes.append(.profile) }
    if !emailVerified { routes.append(.verifyEmail) }
    routes.append(.approve(requestID: requestID))
    return routes
}

Die checks zijn je vangrail. Als prerequisites ontbreken, zet de gebruiker niet op stap 3 met een fout en zonder weg vooruit. Stuur ze naar de eerste ontbrekende stap en zorg dat de back stack nog steeds een coherent verhaal vertelt.

Herstellen van een gedeeltelijk afgeronde flow

Om te herstellen na herstart, sla twee dingen op: de laatst bekende route-state en de door de gebruiker ingevoerde conceptdata. Beslis vervolgens hoe je hervat zonder mensen te verrassen.

Als het concept vers is (minuten of uren), bied dan een duidelijke "Hervatten"-keuze. Als het oud is, begin opnieuw maar gebruik het concept om velden voor te vullen. Als vereisten gewijzigd zijn, bouw het pad opnieuw op met dezelfde vangrails.

Push vs modal: houd de flow makkelijk te verlaten

Centralize flow logic visually
Turn complex business rules into drag-and-drop processes instead of scattered button handlers.
Get Started

Een flow voelt voorspelbaar wanneer er één hoofdmanier vooruit is: schermen pushen op een enkele stack. Gebruik sheets en full-screen covers voor zijtaken, niet voor het hoofdpad.

Push (NavigationStack) past wanneer de gebruiker verwacht dat Terug hun stappen herleidt. Modals (sheet of fullScreenCover) passen wanneer de gebruiker een zijtaak uitvoert, een snelle keuze maakt of een risicovolle actie bevestigt.

Een eenvoudige set regels voorkomt de meeste navigatievreemdheden:

  • Push voor het hoofdpad (Stap 1, Stap 2, Stap 3).
  • Gebruik een sheet voor kleine optionele taken (datum kiezen, land kiezen, document scannen).
  • Gebruik fullScreenCover voor "afzonderlijke werelden" (inloggen, camera-capture, een lang juridisch document).
  • Gebruik een modal voor bevestigingen (flow annuleren, concept verwijderen, indienen ter goedkeuring).

De gebruikelijke fout is het stoppen van hoofdflowschermen in sheets. Als Stap 2 een sheet is, kan de gebruiker het wegvegen en context verliezen, en eindigen met een stack die zegt dat ze op Stap 1 zijn terwijl hun data beweert dat Stap 2 voltooid is.

Bevestigingen zijn het tegenovergestelde: een "Weet u het zeker?"-scherm in de wizard pushen rommelt de stack op en kan lussen creëren (Stap 3 -> Bevestigen -> Terug -> Stap 3 -> Terug -> Bevestigen).

Hoe alles netjes te sluiten na “Klaar”

Bepaal eerst wat “Klaar” betekent: terug naar het startscherm, terug naar de lijst, of een succes-scherm tonen.

Als de flow gepusht is, reset je NavigationPath naar leeg om terug te poppen naar de start. Als de flow modaal gepresenteerd is, roep dismiss() aan vanuit de environment. Als je beide hebt (een modal met een NavigationStack), sluit dan de modal, niet elk gepusht scherm afzonderlijk. Wis na een succesvolle inzending ook het concept zodat een heropende flow fris begint.

Terug-knopgedrag en “Weet u het zeker?”-momenten

Build predictable multi-step flows
Build wizard-style flows with clear steps and stable draft data, without hand-coded navigation.
Try Now

Voor de meeste meerstapsflows is de beste aanpak niets doen: laat de systeem-terugknop (en swipe-back) werken. Het komt overeen met gebruikersverwachting en voorkomt bugs waarbij de UI iets anders zegt dan de navigatiestate.

Interceptie is alleen de moeite waard wanneer teruggaan echt schade zou veroorzaken, zoals het verliezen van een lang ongeslagen formulier of het afbreken van een onomkeerbare actie. Als de gebruiker veilig kan terugkeren en doorgaan, voeg dan geen frictie toe.

Een praktische aanpak is het behouden van systeemnavigatie, maar alleen een bevestiging toevoegen als het scherm “dirty” (aangepast) is. Dat betekent dat je je eigen terugactie aanbiedt en één keer vraagt, met een duidelijke uitweg.

@Environment(\.dismiss) private var dismiss
@State private var showLeaveConfirm = false
let hasUnsavedChanges: Bool

var body: some View {
  Form { /* fields */ }
    .navigationBarBackButtonHidden(hasUnsavedChanges)
    .toolbar {
      if hasUnsavedChanges {
        ToolbarItem(placement: .navigationBarLeading) {
          Button("Back") { showLeaveConfirm = true }
        }
      }
    }
    .confirmationDialog("Discard changes?", isPresented: $showLeaveConfirm) {
      Button("Discard", role: .destructive) { dismiss() }
      Button("Keep Editing", role: .cancel) {}
    }
}

Voorkom dat dit een val wordt:

  • Vraag alleen wanneer je het gevolg in één korte zin kunt uitleggen.
  • Bied een veilige optie (Annuleren, Blijf Bewerken) plus een duidelijke uitgang (Verwijderen, Verlaten).
  • Verberg geen terugknoppen tenzij je ze vervangt door een duidelijke Back of Close.
  • Bevestig bij voorkeur de onomkeerbare actie (zoals "Goedkeuren") in plaats van overal navigatie te blokkeren.

Als je vaak het back-gebaar moet tegenhouden, is dat meestal een teken dat de flow autosave, een opgeslagen concept of kleinere stappen nodig heeft.

Veelvoorkomende fouten die vreemde back-stacks creëren

De meeste “waar ging het naartoe toen ik terugde?”-bugs zijn niet dat SwiftUI random is. Ze komen meestal door patronen die navigatiestate onstabiel maken. Voor voorspelbaar gedrag behandel je de back stack als app-data: stabiel, testbaar en beheerd door één plek.

Onbedoelde extra stacks

Een veelgemaakte valkuil is dat je meerdere NavigationStacks hebt zonder het door te hebben. Bijvoorbeeld: elk tabblad heeft zijn eigen root stack en een child view voegt nog een stack toe binnen de flow. Het resultaat is verwarrend teruggedrag, ontbrekende navigatiebars of schermen die niet op de verwachte manier poppen.

Een andere veelvoorkomende issue is dat je je NavigationPath te vaak opnieuw aanmaakt. Als het pad binnen een view wordt gecreëerd die re-rendered, kan het bij stateveranderingen resetten en de gebruiker terug naar stap 1 springen nadat ze iets in een veld hebben getypt.

De fouten achter de meeste rare stacks zijn eenvoudig:

  • NavigationStack nestelen binnen een andere stack (vaak binnen tabs of sheet-content)
  • NavigationPath() opnieuw initialiseren tijdens view-updates in plaats van in langlevende state te houden
  • Niet-stabiele waarden in je route zetten (zoals een modelobject dat verandert), wat Hashable breekt en mismatched destinations veroorzaakt
  • Navigatiebeslissingen verspreiden over meerdere button-handlers totdat niemand meer kan uitleggen wat “volgende” betekent
  • De flow vanuit meerdere bronnen tegelijk aansturen (bijvoorbeeld zowel een view model als een view die het pad muteren)

Als je data tussen stappen moet doorgeven, gebruik dan stabiele identifiers in de route (IDs, step-enums) en bewaar de feitelijke form-data in gedeelde state.

Een concreet voorbeeld: als je route .profile(User) is en User verandert tijdens het typen, kan SwiftUI het als een andere route zien en de stack herbedraden. Maak de route .profile en bewaar het conceptprofiel in gedeelde state.

Snelle checklist voor voorspelbare navigatie

Test Back behavior early
Prototype the full flow quickly, then refine screens without breaking the user journey.
Prototype Now

Wanneer een flow vreemd aanvoelt, komt dat meestal doordat de back stack niet hetzelfde verhaal vertelt als de gebruiker. Voer voor je de UI perfectioneert een snelle controle op je navigatieregels uit.

Test op een echt apparaat, niet alleen in previews, en probeer zowel langzaam als snel tikken. Snelle tikken onthullen vaak dubbele pushes en ontbrekende state.

  • Ga één stap tegelijk terug van het laatste scherm naar het eerste. Controleer of elk scherm dezelfde data toont die de gebruiker eerder invoerde.
  • Trigger Annuleren vanaf elke stap (inclusief eerste en laatste). Controleer of het altijd naar een logische plek terugkeert, niet naar een willekeurig eerder scherm.
  • Forceer afsluiten halverwege de flow en herstart. Zorg dat je veilig kunt hervatten, hetzij door het pad te herstellen, hetzij door te herstarten bij een bekende stap met opgeslagen data.
  • Open de flow met een deep link of app-snelkoppeling. Verifieer dat de bestemmingsstap geldig is; als vereiste data ontbreekt, stuur door naar de vroegste stap die die data kan verzamelen.
  • Voltooi met Klaar en controleer dat de flow netjes wordt verwijderd. De gebruiker moet niet op Terug kunnen drukken en een voltooide wizard opnieuw binnengaan.

Een simpele test: stel een onboarding-wizard voor met drie schermen (Profiel, Machtigingen, Bevestigen). Voer een naam in, ga vooruit, ga terug, wijzig die, en spring dan naar Bevestigen via een deep link. Als Bevestigen de oude naam toont, of als Terug je naar een duplicaat van Profiel brengt, dan zijn je pad-updates niet consistent.

Als je deze checklist zonder verrassingen doorstaat, voelt je flow rustig en voorspelbaar, zelfs wanneer gebruikers vertrekken en later terugkeren.

Een realistisch voorbeeld en vervolgstappen

Stel je een manager-goedkeuringsflow voor een onkostendeclaratie voor. Die heeft vier stappen: Review, Edit, Confirm en Receipt. De gebruiker verwacht één ding: Terug gaat altijd naar de vorige stap, niet naar een willekeurig eerder bezocht scherm.

Een eenvoudige route-enum houdt dit voorspelbaar. Je NavigationPath zou alleen de route en kleine identifiers moeten opslaan die nodig zijn om state opnieuw te laden, zoals een expenseID en een mode (review vs edit). Vermijd het pushen van grote, muteerbare modellen in het pad omdat dat restores en deep links fragiel maakt.

Houd het werkende concept in één enkele bron van waarheid buiten de views, zoals een @StateObject flow-model (of een store). Elke stap leest en schrijft dat model, zodat schermen verschijnen en verdwijnen zonder invoer te verliezen.

Minimaal houd je drie dingen bij:

  • Routes (bijvoorbeeld: review(expenseID), edit(expenseID), confirm(expenseID), receipt(expenseID))
  • Data (een conceptobject met regelitems en notities, plus een status zoals pending, approved, rejected)
  • Locatie (concept in je flow-model, canoniek record op de server, en een klein restore-token lokaal: expenseID + laatste stap)

Randgevallen zijn waar flows vertrouwen winnen of verliezen. Als de manager in Confirm afkeurt, beslis dan of Terug naar Edit leidt (om te corrigeren) of dat de flow afsluit. Als ze later terugkomen, herstel dan de laatste stap met het opgeslagen token en laad het concept opnieuw. Als ze van apparaat wisselen, behandel de server als bron van waarheid: reconstrueer het pad op basis van serverstatus en stuur ze naar de juiste stap.

Vervolgstappen: documenteer je route-enum (wat elke case betekent en wanneer deze gebruikt wordt), voeg een paar basis-tests toe voor path-building en restore-gedrag, en houd je aan één regel: views bezitten geen navigatiebeslissingen.

Als je hetzelfde soort meerstapsflows bouwt zonder alles vanaf nul te schrijven, passen platforms zoals AppMaster (appmaster.io) dezelfde scheiding toe: houd step-navigation en business-data gescheiden zodat schermen kunnen veranderen zonder de voortgang van de gebruiker te breken.

FAQ

Hoe houd ik de Terug-knop voorspelbaar in een meerstaps SwiftUI-flow?

Use NavigationStack with a single NavigationPath you control. Push exactly one route per “Next” action and pop exactly one route per Back action. When you need a jump (like “Edit profile” from Review), trim the path to a known step instead of pushing more screens.

Waarom verdwijnt mijn formulierdata wanneer ik terug navigeer?

Because SwiftUI rebuilds destination views from the route value, not from a preserved view instance. If your form data lives in the view’s @State, it can reset when the view is recreated. Put draft data in a shared model (like an ObservableObject) that lives outside the pushed views.

Waarom zie ik hetzelfde scherm twee keer in de stack?

It usually happens when you append the same route more than once (often due to fast taps or multiple code paths triggering navigation). Disable the Next button while you’re navigating or while validation/loading runs, and keep navigation mutations in one place so only one append happens per step.

Wat moet ik in mijn route-enum bewaren en wat niet?

Keep routing values small and stable, like an enum case plus lightweight IDs. Store mutable data (the draft) in a separate shared object and look it up by ID if needed. Pushing large, changing models into the path can break Hashable expectations and cause mismatched destinations.

Hoe scheid ik navigation state en flow state netjes?

Navigation is “where the user is,” and flow state is “what they’ve entered.” Own the navigation path in a router (or one top-level state) and own the draft in a separate ObservableObject. Each screen edits the draft; the router only changes steps.

Wat is de veiligste manier om deep links naar stap 3 van een wizard te behandelen?

Treat a deep link as instructions to build a valid sequence of steps, not a teleport to one screen. Build the path by appending required prerequisite steps first (based on what the user has completed), then append the target step. This keeps the back stack coherent and avoids invalid state.

Hoe herstel ik een gedeeltelijk afgeronde flow na het herstarten van de app?

Save two things: the last meaningful route (or step identifier) and the draft data. On relaunch, rebuild the path using the same prerequisite checks you use for deep links, then load the draft. If the draft is old, restarting the flow but prefilling fields is often less surprising than dropping the user mid-wizard.

Wanneer moet ik pushen versus een modal presenteren in een meerstapsflow?

Push screens for the main step-by-step path so Back retraces the flow naturally. Use sheets for optional side tasks and fullScreenCover for separate experiences like login or camera capture. Avoid putting core steps in modals because dismiss gestures can desync the UI from the flow state.

Moet ik het back-gebaar overschrijven om een “Weet u het zeker?”-dialoog te tonen?

Don’t intercept Back by default; let the system behavior work. Add a confirmation only when leaving would lose meaningful unsaved work, and only when the screen is actually “dirty.” Prefer autosave or draft persistence when you find yourself needing confirmations frequently.

Wat zijn de meest voorkomende fouten die rare back-stack bugs veroorzaken?

Common causes are nesting multiple NavigationStacks, recreating NavigationPath during view updates, and having multiple owners mutate the path. Keep one stack per flow, keep the path in long-lived state (@StateObject or a single router), and centralize all push/pop logic to one place.

Gemakkelijk te starten
Maak iets geweldigs

Experimenteer met AppMaster met gratis abonnement.
Als je er klaar voor bent, kun je het juiste abonnement kiezen.

Aan de slag