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.