Development of Navigation with Screen Stack (Navigation Stack) for Mobile Application
Navigation is what user feels every second working with app. Bad screen stack: animation jitters, Back button goes wrong place, deep link opens empty screen — and rating drops. Correct navigation is invisible. Incorrect — annoys.
iOS: UINavigationController and SwiftUI NavigationStack
In UIKit navigation built on UINavigationController. Typical mistake: push from anywhere via UIApplication.shared.windows.first?.rootViewController. Breaks on iPad, in modal contexts and with multiple scenes. Correct: Coordinator pattern, where each coordinator owns its UINavigationController and knows its graph section.
In SwiftUI with iOS 16+ appears NavigationStack with NavigationPath:
@State private var path = NavigationPath()
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: Route.self) { route in
switch route {
case .profile(let id): ProfileView(userId: id)
case .settings: SettingsView()
}
}
}
NavigationPath is type-safe stack that can be saved, restored, and passed through deep link. Before iOS 16 — NavigationView, which has bugs with double push on iPad.
Deep Links on iOS
Universal Links require apple-app-site-association on server and Associated Domains setup in Xcode. URL Schemes (myapp://profile/123) simpler but intercepted by any app. In SceneDelegate.scene(_:openURLContexts:) parse URL → convert to Route → push to right coordinator.
Android: Jetpack Navigation Component
Fragment backstack manually — source of bugs: double transactions, wrong state saving on return. Jetpack Navigation Component (androidx.navigation) replaces manual control with declarative Navigation Graph:
<navigation>
<fragment android:id="@+id/homeFragment" ...>
<action android:id="@+id/action_home_to_profile"
app:destination="@id/profileFragment"/>
</fragment>
<fragment android:id="@+id/profileFragment" ...>
<argument android:name="userId" app:argType="string"/>
</fragment>
</navigation>
Safe argument passing through Safe Args: generated HomeFragmentDirections.actionHomeToProfile(userId) instead of Bundle.putString. Type mismatch — compile error, not runtime crash.
With Compose — NavHost:
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(onProfileClick = { id ->
navController.navigate("profile/$id")
}) }
composable("profile/{userId}") { backStack ->
val userId = backStack.arguments?.getString("userId")!!
ProfileScreen(userId = userId)
}
}
Deep links via <deepLink app:uri="myapp://profile/{userId}"/> in Navigation Graph or via NavDeepLinkBuilder.
React Native: React Navigation
React Navigation is standard for React Native. Stack Navigator, Tab Navigator, Drawer Navigator combine:
const Stack = createNativeStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<TabParamList>();
type RootStackParamList = {
Main: undefined;
Profile: { userId: string };
Settings: undefined;
};
function AppNavigator() {
return (
<NavigationContainer linking={linkingConfig}>
<Stack.Navigator>
<Stack.Screen name="Main" component={TabNavigator} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
createNativeStackNavigator uses native animations (UINavigationController on iOS, Fragment transactions on Android) — faster than JS-animations of createStackNavigator.
Deep links via linking prop: { screens: { Profile: 'profile/:userId' } }.
Flutter: GoRouter
GoRouter officially recommended package for Flutter navigation with web URL and deep link support:
final router = GoRouter(
routes: [
GoRoute(path: '/', builder: (ctx, state) => HomeScreen()),
GoRoute(
path: '/profile/:userId',
builder: (ctx, state) => ProfileScreen(userId: state.pathParameters['userId']!),
),
],
);
context.go('/profile/123') — navigate with stack replacement. context.push('/profile/123') — push over current stack. Works identically on iOS, Android and Flutter Web.
Typical navigation setup problems
State loss on return. On Android Fragment recreated on popBackStack. Solution: FragmentContainerView with saveState = true or ViewModel above navigation level.
Double tap duplicates. Fast double tap on button does double push. On iOS: isMovingToParent check. On Android: currentDestination?.id == R.id.target before navigate. In React Navigation: navigation.navigate idempotent for one screen, navigation.push — not.
Wrong animations on Android. Custom enterAnim/exitAnim via Navigation Component — work. Via FragmentTransaction.setCustomAnimations directly with Navigation — break on popBackStack.
What navigation setup includes
Graph design for functional requirements. Deep link scheme implementation with parameter parsing. Tab/drawer/modal navigation setup. Stack state preservation on tab switch. Navigation scenario testing.
Timeline
Basic navigation (stack + tabs): 2–3 days. Complex navigation with deep links, auth flow, nested navigators: 5–8 days. Cost after requirement analysis.







