I'm currently developing an app using NavigationStack. I wonder where should I put the NavigationPath variable so I can modify it anywhere.
I tried to put it inside the root view as a State variable, but it seems difficult for me to modify it inside deeply-nested views.
struct RootView: View {
@State var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
// ...
}
}
}
struct SubView: View {
var body: some View {
Button("Push to root") {
// how should I access `path` from here?
}
}
}
I also tried to put it as a global variable, but this may result that navigation are shared among all scene, which is not what I intended.
class Router: ObservableObject {
static var shared = Router()
@Published var path = NavigationPath()
}
struct RootView: View {
@ObservedObject var router = Router.shared
var body: some View {
NavigationStack(path: $router.path) {
// ...
}
}
}
struct SubView: View {
@ObservedObject var router = Router.shared
var body: some View {
Button("Push to root") {
router.path = []
}
}
}
I would appreciate if someone were to provide a workable design or any suggestions.
Use a global router like below.
ObservableObject protocol and has a NavigationPath variable.class Router: ObservableObject {
@Published var path: NavigationPath = NavigationPath()
static let shared: Router = Router()
}
struct HomePage: View {
@StateObject var router = Router.shared
// 1. init router with Router.shared.
@StateObject var router = Router.shared
var body: some View {
// 2. init NavigationStack with $router.path
NavigationStack(path: $router.path) {
Button {
// 4. push new page
Router.shared.path.append(category)
} label: {
Text("Hello world")
}
// 3. define navigation destinations
.navigationDestination(for: Category.self, destination: { value in
CategoryPage(category: value)
})
.navigationDestination(for: Product.self, destination: { value in
ProductPage(product: value)
})
}
struct CategoryPage: View {
var body: some View {
Button {
// push new page with Router
Router.shared.path.append(product)
} label: {
Text("Hello world")
}
}
You can use an Environment to pass some action downward:
final class Navigation: ObservableObject {
@Published var path = NavigationPath()
}
extension EnvironmentValues {
private struct NavigationKey: EnvironmentKey {
static let defaultValue = Navigation()
}
var navigation: Navigation {
get { self[NavigationKey.self] }
set { self[NavigationKey.self] = newValue }
}
}
Your rootview then:
struct RootView: View {
@StateObject private var navigation = Navigation()
var body: some View {
NavigationStack(path: $navigation.path) {
// ...
}
.environment(\.navigation, navigation)
}
}
Your subview:
struct SubView: View {
@Environment(\.navigation) private var navigation
var body: some View {
Button("Push to root") {
navigation.path = NavigationPath() // i.e pop to root
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With