navigation
Package navigation provides routing and navigation for the Drift framework.
The package offers two approaches to navigation:
Imperative Navigation with Navigator
Use Navigator for imperative, stack-based navigation where you manually define route generation:
navigation.Navigator{
InitialRoute: "/",
IsRoot: true,
OnGenerateRoute: func(settings navigation.RouteSettings) navigation.Route {
switch settings.Name {
case "/":
return navigation.NewAnimatedPageRoute(buildHome, settings)
case "/details":
return navigation.NewAnimatedPageRoute(buildDetails, settings)
}
return nil
},
}
Declarative Navigation with Router
Use Router for declarative route configuration with automatic path parameter extraction:
navigation.Router{
InitialPath: "/",
Routes: []navigation.ScreenRoute{
{Path: "/", Screen: buildHome},
{Path: "/products/:id", Screen: buildProduct},
},
ErrorBuilder: build404Page,
}
Accessing Navigation
From within the widget tree, use NavigatorOf or RouterOf:
nav := navigation.NavigatorOf(ctx)
nav.PushNamed("/details", args)
From outside the widget tree (deep links, platform callbacks), use RootNavigator:
nav := navigation.RootNavigator()
nav.PushNamed("/details", args)
Route Guards
Both Navigator and Router support redirect callbacks for authentication and authorization:
Redirect: func(ctx navigation.RedirectContext) navigation.RedirectResult {
if !isLoggedIn && strings.HasPrefix(ctx.ToPath, "/protected") {
return navigation.RedirectTo("/login")
}
return navigation.NoRedirect()
},
Tab Navigation
Use TabNavigator for bottom tab navigation with separate navigation stacks per tab. TabNavigator automatically manages which tab's navigator is active for back button handling.
Constants
TransitionDuration is the default duration for page transitions.
const TransitionDuration = 450 * time.Millisecond
Variables
DefaultBarrierColor is the default semi-transparent black used for modal barriers.
var DefaultBarrierColor = graphics.RGBA(0, 0, 0, 128) // 50% opacity black
func HandleBackButton
func HandleBackButton() bool
HandleBackButton attempts to pop the active navigator's route stack.
Call this from platform back button handlers. It tries the active navigator first (e.g., the current tab's navigator), then falls back to the root navigator if the active one can't pop.
Returns true if a route was popped, false if at root (app should exit).
// In platform back button handler:
if !navigation.HandleBackButton() {
// At root - exit app or show confirmation
}
func ParsePath
func ParsePath(fullPath string) (path string, query map[string][]string)
ParsePath splits a URL into its path and query components.
The path is normalized (trailing slash removed) and query parameters are parsed and percent-decoded. URL fragments (#...) are ignored since they are not sent to the server in HTTP requests.
Example:
path, query := navigation.ParsePath("/search?q=hello%20world&page=2#results")
// path = "/search"
// query = {"q": ["hello world"], "page": ["2"]}
func RegisterTabNavigator
func RegisterTabNavigator(ctx core.BuildContext, nav NavigatorState)
RegisterTabNavigator registers a navigator with its enclosing TabNavigator.
This is called automatically by Navigator during Build when inside a TabNavigator. You typically don't need to call this directly.
Registration enables TabNavigator to track which navigator is active and should receive back button events.
func ScreenOnly
func ScreenOnly(build func(core.BuildContext) core.Widget) func(core.BuildContext, RouteSettings) core.Widget
ScreenOnly adapts a plain widget builder to the [ScreenRoute.Screen] signature. Use this for routes that don't need access to path parameters, query strings, or navigation arguments from RouteSettings.
Without ScreenOnly, routes that ignore settings require a boilerplate closure:
navigation.ScreenRoute{
Path: "/settings",
Screen: func(ctx core.BuildContext, _ navigation.RouteSettings) core.Widget {
return buildSettings(ctx)
},
}
With ScreenOnly:
navigation.ScreenRoute{
Path: "/settings",
Screen: navigation.ScreenOnly(buildSettings),
}
Routes that read path parameters or query values should use the full [ScreenRoute.Screen] signature directly.
func ShowModalBottomSheet
func ShowModalBottomSheet(ctx core.BuildContext, builder func(ctx core.BuildContext) core.Widget, options ...BottomSheetOption) <-chan any
ShowModalBottomSheet displays a modal bottom sheet. Returns a buffered channel (size 1) that receives the result when dismissed. The channel is closed after sending the result (or after close without result). Callers can safely read once: result := <-ShowModalBottomSheet(...)
To dismiss the sheet from content, use:
widgets.BottomSheetScope{}.Of(ctx).Close(result)
Example:
result := <-navigation.ShowModalBottomSheet(ctx, func(ctx core.BuildContext) core.Widget {
return widgets.Column{
Children: []core.Widget{
widgets.Text{Content: "Select an option"},
widgets.Button{Label: "Option 1", OnTap: func() {
widgets.BottomSheetScope{}.Of(ctx).Close("option1")
}},
},
}
}, navigation.WithSnapPoints(widgets.SnapHalf, widgets.SnapFull))
type AnimatedPageRoute
AnimatedPageRoute provides a route with animated page transitions.
type AnimatedPageRoute struct {
BaseRoute
// Builder creates the page content.
Builder func(ctx core.BuildContext) core.Widget
// contains filtered or unexported fields
}
func NewAnimatedPageRoute
func NewAnimatedPageRoute(builder func(core.BuildContext) core.Widget, settings RouteSettings) *AnimatedPageRoute
NewAnimatedPageRoute creates an AnimatedPageRoute with the given builder and settings.
func (*AnimatedPageRoute) Build
func (m *AnimatedPageRoute) Build(ctx core.BuildContext) core.Widget
Build returns the page content wrapped in a foreground slide transition. Background slide animation is handled by the navigator.
func (*AnimatedPageRoute) DidPop
func (m *AnimatedPageRoute) DidPop(result any)
DidPop is called when the route is popped.
func (*AnimatedPageRoute) DidPush
func (m *AnimatedPageRoute) DidPush()
DidPush is called when the route is pushed.
func (*AnimatedPageRoute) ForegroundController
func (m *AnimatedPageRoute) ForegroundController() *animation.AnimationController
ForegroundController returns this route's foreground animation controller. Satisfies the AnimatedRoute interface.
func (*AnimatedPageRoute) SetInitialRoute
func (m *AnimatedPageRoute) SetInitialRoute()
SetInitialRoute marks this as the initial route (no animation).
type AnimatedRoute
AnimatedRoute is implemented by routes that have a foreground animation controller. The navigator and other routes use this to query a route's animation state, enabling coordinated transitions (e.g., background slide during push).
type AnimatedRoute interface {
Route
ForegroundController() *animation.AnimationController
}
type BackgroundSlideTransition
BackgroundSlideTransition slides its child to the left as a foreground page enters. At animation value 0 the child is at its normal position; at value 1 the child is shifted left by 33% of the width.
type BackgroundSlideTransition struct {
core.RenderObjectBase
Animation *animation.AnimationController
Child core.Widget
}
func (BackgroundSlideTransition) ChildWidget
func (b BackgroundSlideTransition) ChildWidget() core.Widget
ChildWidget returns the child widget.
func (BackgroundSlideTransition) CreateRenderObject
func (b BackgroundSlideTransition) CreateRenderObject(ctx core.BuildContext) layout.RenderObject
CreateRenderObject creates the renderBackgroundSlideTransition.
func (BackgroundSlideTransition) UpdateRenderObject
func (b BackgroundSlideTransition) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)
UpdateRenderObject updates the renderBackgroundSlideTransition.
type BaseNavigatorObserver
BaseNavigatorObserver provides default no-op implementations.
type BaseNavigatorObserver struct{}
func (*BaseNavigatorObserver) DidPop
func (b *BaseNavigatorObserver) DidPop(route, previousRoute Route)
DidPop is a no-op.
func (*BaseNavigatorObserver) DidPush
func (b *BaseNavigatorObserver) DidPush(route, previousRoute Route)
DidPush is a no-op.
func (*BaseNavigatorObserver) DidRemove
func (b *BaseNavigatorObserver) DidRemove(route, previousRoute Route)
DidRemove is a no-op.
func (*BaseNavigatorObserver) DidReplace
func (b *BaseNavigatorObserver) DidReplace(newRoute, oldRoute Route)
DidReplace is a no-op.
type BaseRoute
BaseRoute provides a default implementation of Route lifecycle methods.
type BaseRoute struct {
// contains filtered or unexported fields
}
func NewBaseRoute
func NewBaseRoute(settings RouteSettings) BaseRoute
NewBaseRoute creates a BaseRoute with the given settings.
func (*BaseRoute) DidChangeNext
func (r *BaseRoute) DidChangeNext(nextRoute Route)
DidChangeNext is a no-op by default.
func (*BaseRoute) DidChangePrevious
func (r *BaseRoute) DidChangePrevious(previousRoute Route)
DidChangePrevious is a no-op by default.
func (*BaseRoute) DidPop
func (r *BaseRoute) DidPop(result any)
DidPop is a no-op by default.
func (*BaseRoute) DidPush
func (r *BaseRoute) DidPush()
DidPush is a no-op by default.
func (*BaseRoute) SetOverlay
func (r *BaseRoute) SetOverlay(overlay OverlayState)
SetOverlay is a no-op for non-modal routes.
func (*BaseRoute) Settings
func (r *BaseRoute) Settings() RouteSettings
Settings returns the route settings.
func (*BaseRoute) WillPop
func (r *BaseRoute) WillPop() bool
WillPop returns true by default, allowing the pop.
type BottomSheetOption
BottomSheetOption configures a bottom sheet shown via ShowModalBottomSheet.
type BottomSheetOption func(*BottomSheetRoute)
func WithBarrierColor
func WithBarrierColor(color graphics.Color) BottomSheetOption
WithBarrierColor sets the barrier color.
func WithBarrierDismissible
func WithBarrierDismissible(dismissible bool) BottomSheetOption
WithBarrierDismissible sets whether tapping the barrier dismisses the sheet.
func WithDragEnabled
func WithDragEnabled(enabled bool) BottomSheetOption
WithDragEnabled sets whether the sheet can be dragged.
func WithDragMode
func WithDragMode(mode widgets.DragMode) BottomSheetOption
WithDragMode sets how the sheet responds to drag gestures.
func WithHandle
func WithHandle(show bool) BottomSheetOption
WithHandle sets whether a drag handle is shown.
func WithInitialSnapPoint
func WithInitialSnapPoint(index int) BottomSheetOption
WithInitialSnapPoint sets the initial snap point index.
func WithSafeArea
func WithSafeArea(use bool) BottomSheetOption
WithSafeArea sets whether the sheet respects the bottom safe area.
func WithSnapBehavior
func WithSnapBehavior(behavior widgets.SnapBehavior) BottomSheetOption
WithSnapBehavior customizes snap thresholds.
func WithSnapPoints
func WithSnapPoints(points ...widgets.SnapPoint) BottomSheetOption
WithSnapPoints sets the snap points for the bottom sheet.
type BottomSheetRoute
BottomSheetRoute displays content as a modal bottom sheet. The sheet slides up from the bottom and can be dismissed by dragging down or tapping the barrier (if enabled).
type BottomSheetRoute struct {
BaseRoute
// SnapPoints defines heights where the sheet can rest.
// If empty, defaults to [widgets.DefaultSnapPoints].
SnapPoints []widgets.SnapPoint
// InitialSnapPoint is the index into SnapPoints where the sheet starts.
// Invalid values are clamped to valid range.
InitialSnapPoint int
// BarrierDismissible controls whether tapping the barrier dismisses the sheet.
// Defaults to true.
BarrierDismissible bool
// BarrierColor is the color of the semi-transparent barrier behind the sheet.
// If nil, uses theme default.
BarrierColor *graphics.Color
// EnableDrag controls whether the sheet can be dragged.
// Defaults to true.
EnableDrag bool
// DragMode controls how drag gestures interact with sheet content.
// Defaults to DragModeAuto.
DragMode widgets.DragMode
// ShowHandle controls whether a drag handle is displayed at the top of the sheet.
// Defaults to true.
ShowHandle bool
// UseSafeArea controls whether the sheet respects the bottom safe area inset.
// Defaults to true.
UseSafeArea bool
// SnapBehavior customizes snapping and dismiss thresholds.
SnapBehavior widgets.SnapBehavior
// contains filtered or unexported fields
}
func NewBottomSheetRoute
func NewBottomSheetRoute(builder func(ctx core.BuildContext) core.Widget, settings RouteSettings) *BottomSheetRoute
NewBottomSheetRoute creates a new BottomSheetRoute with sensible defaults.
func (*BottomSheetRoute) Build
func (r *BottomSheetRoute) Build(ctx core.BuildContext) core.Widget
Build returns the widget for this route. When overlay is available and entries inserted, returns a placeholder.
func (*BottomSheetRoute) DidPop
func (r *BottomSheetRoute) DidPop(result any)
DidPop is called when the route is popped from the navigator. This triggers the exit animation if not already animating.
func (*BottomSheetRoute) DidPush
func (r *BottomSheetRoute) DidPush()
DidPush is called when the route is pushed onto the navigator.
func (*BottomSheetRoute) IsTransparent
func (r *BottomSheetRoute) IsTransparent() bool
IsTransparent returns true - bottom sheets show content behind the barrier.
func (*BottomSheetRoute) SetOverlay
func (r *BottomSheetRoute) SetOverlay(o OverlayState)
SetOverlay is called by Navigator when OverlayState becomes available.
type CaseSensitivity
CaseSensitivity controls whether path matching is case-sensitive.
type CaseSensitivity int
const (
// CaseSensitive requires exact case match in path segments.
// This is the default behavior: "/Products" does NOT match "/products".
CaseSensitive CaseSensitivity = iota
// CaseInsensitive ignores case when matching path segments.
// With this setting, "/Products" matches "/products".
CaseInsensitive
)
type DeepLinkController
DeepLinkController listens for deep links and navigates to matching routes.
Deep links are dispatched via RootNavigator, which requires a Router or Navigator with IsRoot=true to be present in the widget tree. If your app uses TabNavigator at the top level, wrap it in a Router or Navigator:
navigation.Router{
Routes: []navigation.ScreenRoute{
{Path: "/", Screen: buildTabNavigator},
},
}
Without a root navigator, deep links will remain pending indefinitely.
type DeepLinkController struct {
RouteForLink func(link platform.DeepLink) (DeepLinkRoute, bool)
OnError func(err error)
// contains filtered or unexported fields
}
func NewDeepLinkController
func NewDeepLinkController(routeForLink func(platform.DeepLink) (DeepLinkRoute, bool), onError func(error)) *DeepLinkController
NewDeepLinkController creates a controller with the route mapper and immediately starts listening for deep links.
func (*DeepLinkController) Stop
func (c *DeepLinkController) Stop()
Stop stops listening for deep links.
type DeepLinkRoute
DeepLinkRoute describes a navigation target from a deep link.
type DeepLinkRoute struct {
Name string
Args any
}
type FadeTransition
FadeTransition animates the opacity of its child.
type FadeTransition struct {
core.RenderObjectBase
Animation *animation.AnimationController
Child core.Widget
}
func (FadeTransition) ChildWidget
func (f FadeTransition) ChildWidget() core.Widget
ChildWidget returns the child widget.
func (FadeTransition) CreateRenderObject
func (f FadeTransition) CreateRenderObject(ctx core.BuildContext) layout.RenderObject
CreateRenderObject creates the RenderFadeTransition.
func (FadeTransition) UpdateRenderObject
func (f FadeTransition) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)
UpdateRenderObject updates the RenderFadeTransition.
type ModalRoute
ModalRoute is a route that displays as a modal overlay with a barrier. The modal content appears above a semi-transparent barrier that can optionally dismiss the modal when tapped.
type ModalRoute struct {
BaseRoute
// BarrierDismissible controls whether tapping the barrier dismisses the modal.
BarrierDismissible bool
// BarrierColor is the color of the semi-transparent barrier.
// If nil, defaults to DefaultBarrierColor.
// Set to a pointer to graphics.Color(0) for a fully transparent barrier.
BarrierColor *graphics.Color
// BarrierLabel is the accessibility label for the barrier.
BarrierLabel string
// contains filtered or unexported fields
}
func NewModalRoute
func NewModalRoute(builder func(ctx core.BuildContext) core.Widget, settings RouteSettings) *ModalRoute
NewModalRoute creates a new ModalRoute with the given builder and settings. By default, the barrier is dismissible and uses DefaultBarrierColor.
func (*ModalRoute) Build
func (r *ModalRoute) Build(ctx core.BuildContext) core.Widget
Build returns the widget for this route. When overlay is available and entries inserted, returns a placeholder. Falls back to direct rendering if no overlay is available.
func (*ModalRoute) DidPop
func (r *ModalRoute) DidPop(result any)
DidPop is called when the route is popped from the navigator.
func (*ModalRoute) DidPush
func (r *ModalRoute) DidPush()
DidPush is called when the route is pushed onto the navigator.
func (*ModalRoute) IsTransparent
func (r *ModalRoute) IsTransparent() bool
IsTransparent returns true - modals show content behind the barrier.
func (*ModalRoute) SetOverlay
func (r *ModalRoute) SetOverlay(o OverlayState)
SetOverlay is called by Navigator when OverlayState becomes available.
type NavigationScope
NavigationScope tracks navigator hierarchy and determines which navigator receives back button events and deep links.
In apps with multiple navigators (e.g., TabNavigator with per-tab stacks), NavigationScope ensures the correct navigator handles user input:
- The root navigator (IsRoot=true) handles deep links
- The active navigator (set by TabNavigator on tab change) handles back button
- When the active navigator can't pop, falls back to root
You typically don't interact with NavigationScope directly. It's managed automatically by Navigator and TabNavigator.
type NavigationScope struct {
// contains filtered or unexported fields
}
func (*NavigationScope) ActiveNavigator
func (s *NavigationScope) ActiveNavigator() NavigatorState
ActiveNavigator returns the currently focused navigator.
func (*NavigationScope) ClearActiveIf
func (s *NavigationScope) ClearActiveIf(nav NavigatorState)
ClearActiveIf clears activeNavigator if it matches nav (call in Dispose).
func (*NavigationScope) ClearRootIf
func (s *NavigationScope) ClearRootIf(nav NavigatorState)
ClearRootIf clears root navigator if it matches nav (call in Dispose).
func (*NavigationScope) SetActiveNavigator
func (s *NavigationScope) SetActiveNavigator(nav NavigatorState)
SetActiveNavigator sets which navigator receives back button events. TabNavigator calls this on tab change.
func (*NavigationScope) SetRoot
func (s *NavigationScope) SetRoot(nav NavigatorState)
SetRoot registers the root navigator (called by Navigator with IsRoot=true).
type Navigator
Navigator manages a stack of routes using imperative navigation.
Navigator provides push/pop stack semantics similar to mobile navigation patterns. For declarative route configuration, see Router instead.
Basic usage:
navigation.Navigator{
InitialRoute: "/",
IsRoot: true, // Required for back button handling
OnGenerateRoute: func(settings navigation.RouteSettings) navigation.Route {
switch settings.Name {
case "/":
return navigation.NewAnimatedPageRoute(buildHome, settings)
case "/details":
return navigation.NewAnimatedPageRoute(buildDetails, settings)
}
return nil
},
}
With route guards for authentication:
navigation.Navigator{
InitialRoute: "/",
IsRoot: true,
RefreshListenable: authState, // Re-evaluate on auth changes
Redirect: func(ctx navigation.RedirectContext) navigation.RedirectResult {
if !authState.IsLoggedIn() && isProtectedRoute(ctx.ToPath) {
return navigation.RedirectTo("/login")
}
return navigation.NoRedirect()
},
OnGenerateRoute: generateRoute,
}
type Navigator struct {
core.StatefulBase
// InitialRoute is the name of the first route to display.
InitialRoute string
// OnGenerateRoute creates routes from route settings.
OnGenerateRoute func(settings RouteSettings) Route
// OnUnknownRoute is called when OnGenerateRoute returns nil.
OnUnknownRoute func(settings RouteSettings) Route
// Observers receive navigation events.
Observers []NavigatorObserver
// IsRoot marks this as the app's primary navigator.
// Only root navigators register with the NavigationScope for back button handling.
// Set this to true for your main navigator, false for nested navigators (e.g., in tabs).
IsRoot bool
// Redirect is called before every navigation.
// Return NoRedirect() to allow navigation, or RedirectTo()/RedirectWithArgs() to redirect.
// Only applies to named routes (Push with non-empty Settings().Name, PushNamed, etc.).
Redirect func(ctx RedirectContext) RedirectResult
// RefreshListenable triggers redirect re-evaluation when notified.
// Use this when auth state changes to re-check if the current route is still accessible.
RefreshListenable core.Listenable
}
func (Navigator) CreateState
func (n Navigator) CreateState() core.State
CreateState creates the NavigatorState.
type NavigatorObserver
NavigatorObserver observes navigation events.
type NavigatorObserver interface {
// DidPush is called when a route is pushed.
DidPush(route, previousRoute Route)
// DidPop is called when a route is popped.
DidPop(route, previousRoute Route)
// DidRemove is called when a route is removed.
DidRemove(route, previousRoute Route)
// DidReplace is called when a route is replaced.
DidReplace(newRoute, oldRoute Route)
}
type NavigatorState
NavigatorState provides methods to manipulate the navigation stack.
Obtain a NavigatorState using NavigatorOf from within the widget tree, or RootNavigator from outside it (e.g., for deep links).
nav := navigation.NavigatorOf(ctx)
nav.PushNamed("/details", map[string]any{"id": 123})
type NavigatorState interface {
// Push adds a route to the top of the stack.
// If a Redirect callback is configured and the route has a name,
// the redirect logic is applied before pushing.
Push(route Route)
// PushNamed creates and pushes a route by name.
// The route is created via OnGenerateRoute with the given name and args.
// Redirect logic is applied if configured.
PushNamed(name string, args any)
// PushReplacementNamed replaces the current route with a new named route.
// The old route receives DidPop, the new route receives DidPush.
// Redirect logic is applied if configured.
PushReplacementNamed(name string, args any)
// Pop removes the current route from the stack.
// The result is passed to the popped route's DidPop callback.
// Does nothing if only one route remains (can't pop the root).
Pop(result any)
// PopUntil removes routes until the predicate returns true for the top route.
// Each route's WillPop is checked before removal; removal stops if WillPop
// returns false. Routes are removed without animation.
PopUntil(predicate func(Route) bool)
// PushReplacement replaces the current route with a new route.
// If a Redirect callback is configured and the route has a name,
// the redirect logic is applied before replacing.
PushReplacement(route Route)
// CanPop returns true if there are routes that can be popped.
// Returns false if only the root route remains.
CanPop() bool
// MaybePop attempts to pop if possible.
// Checks CanPop and the top route's WillPop before popping.
// Returns true if a route was popped, false otherwise.
MaybePop(result any) bool
}
func NavigatorOf
func NavigatorOf(ctx core.BuildContext) NavigatorState
NavigatorOf returns the NavigatorState from the nearest Navigator ancestor. Returns nil if no Navigator is found.
func RootNavigator
func RootNavigator() NavigatorState
RootNavigator returns the root navigator registered with IsRoot=true.
Use this for deep links and external navigation that should always target the main app navigator, regardless of which tab or nested navigator is currently active.
// In a deep link handler:
if nav := navigation.RootNavigator(); nav != nil {
nav.PushNamed("/product", map[string]any{"id": productID})
}
Returns nil if no root navigator has been registered.
type OverlayState
OverlayState is an alias for overlay.OverlayState for convenience.
type OverlayState = overlay.OverlayState
type PageRoute
PageRoute is a simpler route without transitions.
type PageRoute struct {
BaseRoute
// Builder creates the page content.
Builder func(ctx core.BuildContext) core.Widget
}
func NewPageRoute
func NewPageRoute(builder func(core.BuildContext) core.Widget, settings RouteSettings) *PageRoute
NewPageRoute creates a PageRoute with the given builder and settings.
func (*PageRoute) Build
func (p *PageRoute) Build(ctx core.BuildContext) core.Widget
Build returns the page content.
type PathPattern
PathPattern represents a compiled URL pattern for route matching.
Patterns support three types of segments:
- Static: "/products", "/users/list" - must match exactly
- Parameter: ":id", ":name" - captures a single path segment
- Wildcard: "*path" - captures all remaining segments (must be last)
Create patterns using NewPathPattern:
pattern := navigation.NewPathPattern("/products/:id")
params, ok := pattern.Match("/products/123")
// params = {"id": "123"}, ok = true
type PathPattern struct {
// contains filtered or unexported fields
}
func NewPathPattern
func NewPathPattern(pattern string, opts ...PathPatternOption) *PathPattern
NewPathPattern compiles a pattern string into a PathPattern. Pattern syntax:
- Static segments: /products, /users
- Parameters: :id, :name (captures a single segment)
- Wildcards: *path (captures remaining path, must be last)
Examples:
- "/products/:id" matches "/products/123" -> {"id": "123"}
- "/files/*path" matches "/files/a/b/c" -> {"path": "a/b/c"}
func (*PathPattern) Match
func (p *PathPattern) Match(path string) (params map[string]string, ok bool)
Match checks if a path matches this pattern and extracts parameters.
Returns the extracted parameters and true if the path matches, or nil and false if it doesn't match. Percent-encoded values (like %20 for space) are automatically decoded in the returned parameters.
Examples:
pattern := navigation.NewPathPattern("/products/:id")
params, ok := pattern.Match("/products/123")
// params = {"id": "123"}, ok = true
params, ok := pattern.Match("/products/hello%20world")
// params = {"id": "hello world"}, ok = true
params, ok := pattern.Match("/users/123")
// params = nil, ok = false
func (*PathPattern) Pattern
func (p *PathPattern) Pattern() string
Pattern returns the original pattern string used to create this PathPattern.
type PathPatternOption
PathPatternOption configures a PathPattern during creation.
type PathPatternOption func(*PathPattern)
func WithCaseSensitivity
func WithCaseSensitivity(sensitivity CaseSensitivity) PathPatternOption
WithCaseSensitivity sets whether matching is case-sensitive.
// Case-insensitive matching
pattern := navigation.NewPathPattern("/Products/:id",
navigation.WithCaseSensitivity(navigation.CaseInsensitive),
)
func WithTrailingSlash
func WithTrailingSlash(behavior TrailingSlashBehavior) PathPatternOption
WithTrailingSlash sets how trailing slashes are handled during matching.
// Strict mode - trailing slash must match exactly
pattern := navigation.NewPathPattern("/products/:id",
navigation.WithTrailingSlash(navigation.TrailingSlashStrict),
)
type RedirectContext
RedirectContext provides information about the navigation being attempted, allowing the Redirect callback to make informed decisions.
type RedirectContext struct {
// FromPath is the current route's path before navigation.
// Empty string on initial route or when navigating from outside a route.
FromPath string
// ToPath is the intended destination path.
ToPath string
// Arguments are the navigation arguments being passed.
Arguments any
}
type RedirectResult
RedirectResult tells the navigator how to handle the navigation.
Create using helper functions:
- NoRedirect to allow navigation to proceed
- RedirectTo to redirect to a different path
- RedirectWithArgs to redirect with custom arguments
type RedirectResult struct {
// Path is the redirect destination. Empty string means no redirect.
Path string
// Arguments for the redirect destination.
// If nil, original arguments are discarded.
Arguments any
// Replace controls whether to replace the current route (true) or push (false).
// RedirectTo and RedirectWithArgs set this to true by default.
Replace bool
}
func NoRedirect
func NoRedirect() RedirectResult
NoRedirect returns a result that allows navigation to proceed normally. Use this in your Redirect callback when the route should be accessible.
Redirect: func(ctx navigation.RedirectContext) navigation.RedirectResult {
if isPublicRoute(ctx.ToPath) {
return navigation.NoRedirect()
}
// ... check auth
}
func RedirectTo
func RedirectTo(path string) RedirectResult
RedirectTo creates a redirect to a different path. The current route is replaced (not pushed) by default.
Redirect: func(ctx navigation.RedirectContext) navigation.RedirectResult {
if !isLoggedIn {
return navigation.RedirectTo("/login")
}
return navigation.NoRedirect()
}
func RedirectWithArgs
func RedirectWithArgs(path string, args any) RedirectResult
RedirectWithArgs creates a redirect with custom arguments. Useful for preserving the intended destination through a login flow.
Redirect: func(ctx navigation.RedirectContext) navigation.RedirectResult {
if !isLoggedIn && isProtected(ctx.ToPath) {
return navigation.RedirectWithArgs("/login", map[string]any{
"returnTo": ctx.ToPath,
})
}
return navigation.NoRedirect()
}
type Route
Route represents a screen in the navigation stack.
type Route interface {
// Build creates the widget for this route.
Build(ctx core.BuildContext) core.Widget
// Settings returns the route configuration.
Settings() RouteSettings
// DidPush is called when the route is pushed onto the navigator.
DidPush()
// DidPop is called when the route is popped from the navigator.
DidPop(result any)
// DidChangeNext is called when the next route in the stack changes.
DidChangeNext(nextRoute Route)
// DidChangePrevious is called when the previous route in the stack changes.
DidChangePrevious(previousRoute Route)
// WillPop is called before the route is popped.
// Return false to prevent the pop.
WillPop() bool
// SetOverlay is called by Navigator when OverlayState becomes available.
// Routes that use overlay entries store this reference.
SetOverlay(overlay OverlayState)
}
type RouteSettings
RouteSettings contains configuration and parameters for a route.
When using the declarative Router, Params and Query are automatically populated from the URL. When using Navigator directly, you can populate these fields manually in OnGenerateRoute.
type RouteSettings struct {
// Name is the route path (e.g., "/home", "/products/123").
Name string
// Arguments contains arbitrary data passed during navigation.
// Use this for complex objects that don't fit in the URL.
Arguments any
// Params contains path parameters extracted from the URL.
// For example, "/products/:id" matching "/products/123" yields {"id": "123"}.
// Values are automatically percent-decoded.
Params map[string]string
// Query contains query string parameters from the URL.
// Supports multiple values per key (e.g., "?tag=a&tag=b").
// Values are automatically percent-decoded.
Query map[string][]string
}
func MatchPath
func MatchPath(pattern *PathPattern, fullPath string) (settings RouteSettings, ok bool)
MatchPath is a convenience function that combines path parsing and PathPattern.Match to extract complete RouteSettings from a URL.
Unlike ParsePath, this function preserves trailing slashes for matching, allowing TrailingSlashStrict patterns to work correctly.
Returns RouteSettings with Name, Params, and Query populated if the path matches, or empty settings and false if it doesn't match.
Example:
pattern := navigation.NewPathPattern("/products/:id")
settings, ok := navigation.MatchPath(pattern, "/products/123?color=red")
// settings.Name = "/products/123?color=red"
// settings.Params = {"id": "123"}
// settings.Query = {"color": ["red"]}
// ok = true
func (RouteSettings) Param
func (s RouteSettings) Param(key string) string
Param returns a path parameter value or empty string if not found.
func (RouteSettings) QueryValue
func (s RouteSettings) QueryValue(key string) string
QueryValue returns the first query parameter value or empty string if not found.
func (RouteSettings) QueryValues
func (s RouteSettings) QueryValues(key string) []string
QueryValues returns all query parameter values for a key.
type Router
Router provides declarative route configuration with automatic path matching and parameter extraction.
Router is the recommended approach for apps with URL-based routing. It internally creates a Navigator with IsRoot=true, so you don't need to manage navigator registration manually.
IMPORTANT: Router is designed to be used as a singleton at the root of your app. Do not nest Routers or use Router inside TabNavigator tabs. For tabs with their own navigation stacks, use Navigator with [Tab.OnGenerateRoute] instead.
Basic usage:
navigation.Router{
InitialPath: "/",
Routes: []navigation.ScreenRoute{
{Path: "/", Screen: buildHome},
{Path: "/products/:id", Screen: buildProduct},
},
ErrorBuilder: build404Page,
}
With authentication:
navigation.Router{
InitialPath: "/",
RefreshListenable: authState,
Redirect: func(ctx navigation.RedirectContext) navigation.RedirectResult {
if !authState.IsLoggedIn() && isProtected(ctx.ToPath) {
return navigation.RedirectTo("/login")
}
return navigation.NoRedirect()
},
Routes: routes,
}
Access the router for navigation using RouterOf:
router := navigation.RouterOf(ctx)
router.Go("/products/123", nil)
type Router struct {
core.StatefulBase
// Routes defines the route tree.
Routes []ScreenRoute
// Redirect is the global redirect callback, checked before every navigation.
// Return [NoRedirect] to allow, or [RedirectTo]/[RedirectWithArgs] to redirect.
// Route-specific redirects in [ScreenRoute.Redirect] are checked after this.
Redirect func(ctx RedirectContext) RedirectResult
// ErrorBuilder creates a widget for unmatched routes (404 pages).
// If nil, navigation to unknown routes is silently ignored.
ErrorBuilder func(ctx core.BuildContext, settings RouteSettings) core.Widget
// InitialPath is the starting route path.
// Defaults to "/" if not specified.
InitialPath string
// TrailingSlashBehavior controls how trailing slashes are handled in matching.
// Default is [TrailingSlashStrip] which treats "/path/" same as "/path".
TrailingSlashBehavior TrailingSlashBehavior
// CaseSensitivity controls case handling in path matching.
// Default is [CaseSensitive] which requires exact case match.
CaseSensitivity CaseSensitivity
// RefreshListenable triggers redirect re-evaluation when notified.
// Connect this to auth state changes to automatically redirect users
// when they log in or out.
RefreshListenable core.Listenable
}
func (Router) CreateState
func (r Router) CreateState() core.State
CreateState creates the RouterState.
type RouterState
RouterState extends NavigatorState with path-based navigation methods.
Obtain a RouterState using RouterOf from within the widget tree. RouterState embeds NavigatorState, so all standard navigation methods (Push, Pop, etc.) are available.
router := navigation.RouterOf(ctx)
router.Go("/products/123", nil) // Navigate to path
router.Replace("/home", nil) // Replace current route
router.Pop(nil) // Go back
type RouterState interface {
NavigatorState // All NavigatorState methods are available
// Go navigates to the given path, pushing a new route onto the stack.
// Equivalent to PushNamed but with clearer intent for URL-based navigation.
Go(path string, args any)
// Replace replaces the current route with the given path.
// The current route is removed and the new route takes its place.
// Equivalent to PushReplacementNamed.
Replace(path string, args any)
}
func RouterOf
func RouterOf(ctx core.BuildContext) RouterState
RouterOf returns the RouterState from the nearest Router ancestor.
Use this for navigation from within the widget tree when you need the Router's path-based methods (Go, Replace). Returns nil if no Router ancestor exists.
func handleProductTap(ctx core.BuildContext, productID string) {
if router := navigation.RouterOf(ctx); router != nil {
router.Go("/products/"+productID, nil)
}
}
RouterState embeds NavigatorState, so you can also use standard methods:
router := navigation.RouterOf(ctx)
router.Pop(nil) // Go back
type ScreenRoute
ScreenRoute defines a route in the declarative Router.
A ScreenRoute can serve as a leaf route (with Screen), a layout wrapper (with Wrap), or both. Routes with only Children act as prefix groups.
Path patterns support:
- Static segments: "/products", "/users/list"
- Parameters: "/products/:id", "/users/:userId/posts/:postId"
- Wildcards: "/files/*path" (captures remaining path)
Example with nested routes:
navigation.ScreenRoute{
Path: "/products",
Screen: buildProductList,
Children: []navigation.ScreenRoute{
{
Path: "/:id", // Matches /products/:id
Screen: buildProductDetail,
},
{
Path: "/:id/reviews", // Matches /products/:id/reviews
Screen: buildProductReviews,
},
},
}
Wrap child routes in a persistent layout (tabs, sidebars, etc.):
navigation.ScreenRoute{
Wrap: func(ctx core.BuildContext, child core.Widget) core.Widget {
return widgets.Column{
Children: []core.Widget{
MyNavigationBar{},
widgets.Expanded{Child: child},
},
}
},
Children: []navigation.ScreenRoute{
{Path: "/home", Screen: buildHome},
{Path: "/profile", Screen: buildProfile},
},
}
type ScreenRoute struct {
// Path is the URL pattern for this route.
// Use :param for path parameters and *param for wildcards.
// Nested routes inherit the parent's path as a prefix.
Path string
// Screen creates the widget for this route.
// RouteSettings includes extracted Params and Query from the URL.
// Nil for pure wrapper or prefix-group routes.
Screen func(ctx core.BuildContext, settings RouteSettings) core.Widget
// Wrap wraps child routes in a persistent layout.
// The child parameter is the matched child route's widget.
// Wrap applies only to Children, not to this route's own Screen.
// Nil for leaf-only routes.
Wrap func(ctx core.BuildContext, child core.Widget) core.Widget
// Redirect defines redirect logic for this route and its descendants.
// Checked after the Router's global Redirect callback.
// Ancestor redirects are evaluated outermost-first before the
// matched route's own Redirect.
Redirect func(ctx RedirectContext) RedirectResult
// Children defines nested child routes.
// Child paths are concatenated with this route's path.
// If Wrap is set, all children are wrapped by it.
Children []ScreenRoute
}
type SlideDirection
SlideDirection determines the direction of a slide transition.
type SlideDirection int
const (
// SlideFromRight slides content in from the right.
SlideFromRight SlideDirection = iota
// SlideFromLeft slides content in from the left.
SlideFromLeft
// SlideFromBottom slides content in from the bottom.
SlideFromBottom
// SlideFromTop slides content in from the top.
SlideFromTop
)
type SlideTransition
SlideTransition animates a child sliding from a direction.
type SlideTransition struct {
core.RenderObjectBase
Animation *animation.AnimationController
Direction SlideDirection
Child core.Widget
}
func (SlideTransition) ChildWidget
func (s SlideTransition) ChildWidget() core.Widget
ChildWidget returns the child widget.
func (SlideTransition) CreateRenderObject
func (s SlideTransition) CreateRenderObject(ctx core.BuildContext) layout.RenderObject
CreateRenderObject creates the RenderSlideTransition.
func (SlideTransition) UpdateRenderObject
func (s SlideTransition) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)
UpdateRenderObject updates the RenderSlideTransition.
type Tab
Tab configures a single tab in a TabNavigator.
For simple tabs with a single screen, use NewTab. For tabs with their own navigation stack, configure OnGenerateRoute.
type Tab struct {
// Item defines the tab's appearance in the tab bar.
Item widgets.TabItem
// Builder creates the tab's root widget.
// Used when OnGenerateRoute is nil to create a simple single-screen tab.
Builder func(ctx core.BuildContext) core.Widget
// InitialRoute is the starting route for this tab's navigator.
// Defaults to "/" if not specified.
InitialRoute string
// OnGenerateRoute creates routes for this tab's navigation stack.
// If nil, a simple navigator is created using Builder for the initial route.
OnGenerateRoute func(settings RouteSettings) Route
// OnUnknownRoute handles navigation to undefined routes within this tab.
OnUnknownRoute func(settings RouteSettings) Route
// Observers receive navigation events for this tab's navigator.
Observers []NavigatorObserver
}
func NewTab
func NewTab(item widgets.TabItem, builder func(ctx core.BuildContext) core.Widget) Tab
NewTab creates a Tab with a simple root builder.
Use this for tabs that don't need their own navigation stack. For tabs with multiple screens, create a Tab with OnGenerateRoute instead.
navigation.NewTab(
widgets.TabItem{Label: "Home", Icon: homeIcon},
buildHomeScreen,
)
type TabController
TabController coordinates tab selection state.
type TabController struct {
// contains filtered or unexported fields
}
func NewTabController
func NewTabController(initialIndex int) *TabController
NewTabController creates a controller with the initial index.
func (*TabController) AddListener
func (c *TabController) AddListener(listener func(int)) func()
AddListener registers a listener. Returns an unsubscribe function.
func (*TabController) Index
func (c *TabController) Index() int
Index returns the current tab index.
func (*TabController) SetIndex
func (c *TabController) SetIndex(index int)
SetIndex updates the active tab index.
type TabNavigator
TabNavigator provides bottom tab navigation with separate navigation stacks per tab.
Each tab has its own Navigator, allowing independent navigation within tabs. When the user switches tabs, the tab's navigation state is preserved. TabNavigator automatically manages which tab's navigator is "active" for back button handling via NavigationScope.
Basic usage:
navigation.TabNavigator{
Tabs: []navigation.Tab{
navigation.NewTab(
widgets.TabItem{Label: "Home"},
buildHomeScreen,
),
navigation.NewTab(
widgets.TabItem{Label: "Profile"},
buildProfileScreen,
),
},
}
With navigation stacks per tab:
navigation.Tab{
Item: widgets.TabItem{Label: "Products"},
InitialRoute: "/products",
OnGenerateRoute: func(settings navigation.RouteSettings) navigation.Route {
switch settings.Name {
case "/products":
return navigation.NewAnimatedPageRoute(buildProductList, settings)
case "/products/detail":
return navigation.NewAnimatedPageRoute(buildProductDetail, settings)
}
return nil
},
}
Accessibility: Inactive tabs are automatically excluded from the accessibility tree using [widgets.ExcludeSemantics].
type TabNavigator struct {
core.StatefulBase
// Tabs defines the tab configuration. At least one tab is required.
Tabs []Tab
// Controller optionally provides programmatic control over tab selection.
// If nil, a default controller starting at index 0 is created.
Controller *TabController
}
func (TabNavigator) CreateState
func (t TabNavigator) CreateState() core.State
type TrailingSlashBehavior
TrailingSlashBehavior controls how trailing slashes are handled during path matching.
type TrailingSlashBehavior int
const (
// TrailingSlashStrip removes trailing slashes before matching.
// This is the default behavior, making "/products/1/" match "/products/:id".
TrailingSlashStrip TrailingSlashBehavior = iota
// TrailingSlashStrict requires exact trailing slash match.
// With this setting, "/products/1/" does NOT match "/products/:id".
TrailingSlashStrict
)
type TransparentRoute
TransparentRoute is implemented by routes that should keep previous routes visible. Routes like bottom sheets and dialogs that have semi-transparent barriers should implement this and return true from IsTransparent.
type TransparentRoute interface {
Route
// IsTransparent returns true if previous routes should remain visible.
IsTransparent() bool
}
Generated by gomarkdoc