Skip to main content
Path parameters allow you to capture dynamic segments from URLs. They’re perfect for routes like /project/:id or /user/:username where the value changes but the route structure remains the same.

Defining path parameters

Path parameters are denoted by a colon (:) followed by a parameter name in the URL pattern:
NavigationData(
  label: ProjectPage.name,
  url: '/project/:projectId',
  builder: (context, routeData, globalData) => ProjectPage(
    id: int.tryParse(routeData.pathParameters['projectId'] ?? ''),
  ),
)
From the example app in example/lib/main.dart:14-16:
NavigationData(
    label: ProjectPage.name,
    url: '/project/:id',
    builder: (context, routeData, globalData) => ProjectPage(
        id: int.tryParse(routeData.pathParameters['id'] ?? '') ?? 0)),

Accessing path parameters

Path parameters are available via routeData.pathParameters in your builder. They’re stored in a Map<String, String>:
NavigationData(
  url: '/user/:username/posts/:postId',
  builder: (context, routeData, globalData) => PostPage(
    username: routeData.pathParameters['username'] ?? '',
    postId: int.tryParse(routeData.pathParameters['postId'] ?? '') ?? 0,
  ),
)
There are two ways to navigate with path parameters:

Direct path navigation

Include the parameter value directly in the path:
NavigationManager.instance.push('/project/320');
From the example app in example/lib/main.dart:77-79:
MaterialButton(
  onPressed: () => NavigationManager.instance.push('/project/1'),
  child: const Text('Open Project Path'),
),

Named navigation with pathParameters

Use the pathParameters map when navigating by name:
NavigationManager.instance.push(
  ProjectPage.name,
  pathParameters: {'projectId': '320'},
);
From the example app in example/lib/main.dart:81-84:
MaterialButton(
  onPressed: () => NavigationManager.instance
      .push(ProjectPage.name, pathParameters: {'id': '2'}),
  child: const Text('Open Project Named'),
),

Multiple path parameters

Define routes with multiple dynamic segments:
NavigationData(
  url: '/company/:companyId/project/:projectId',
  builder: (context, routeData, globalData) => ProjectDetailsPage(
    companyId: routeData.pathParameters['companyId'] ?? '',
    projectId: routeData.pathParameters['projectId'] ?? '',
  ),
)
Navigate to it:
// Using path
NavigationManager.instance.push('/company/123/project/456');

// Using pathParameters
NavigationManager.instance.push(
  ProjectDetailsPage.name,
  pathParameters: {
    'companyId': '123',
    'projectId': '456',
  },
);

Type conversion

Path parameters are always Strings because they come from URLs. Convert them to the appropriate type in your builder.

Integers

int id = int.tryParse(routeData.pathParameters['id'] ?? '') ?? 0;

Custom types

Parse path parameters into custom types:
enum ProjectType { personal, business, enterprise }

ProjectType? parseProjectType(String? value) {
  return ProjectType.values.firstWhereOrNull(
    (type) => type.name == value,
  );
}

NavigationData(
  url: '/project/:type/:id',
  builder: (context, routeData, globalData) => ProjectPage(
    type: parseProjectType(routeData.pathParameters['type']),
    id: int.tryParse(routeData.pathParameters['id'] ?? '') ?? 0,
  ),
)

Path parameter rules

Be aware of these important rules when working with path parameters:

Different URLs require different routes

/project and /project/:projectId are different URLs and require separate route definitions:
// Define both routes
NavigationData(
  url: '/project',
  builder: (context, routeData, globalData) => ProjectListPage(),
),
NavigationData(
  url: '/project/:projectId',
  builder: (context, routeData, globalData) => ProjectDetailsPage(
    id: int.tryParse(routeData.pathParameters['projectId'] ?? '') ?? 0,
  ),
),

Trailing slashes

A trailing slash like /project/ is equivalent to /project, not /project/:id:
// These are equivalent
'/project'
'/project/'

// This is different
'/project/:id'

Parameter names must match

The parameter name in your URL must match the key you use to access it:
// ✅ Correct - names match
NavigationData(
  url: '/user/:userId',
  builder: (context, routeData, globalData) => UserPage(
    id: routeData.pathParameters['userId'], // ✅
  ),
)

// ❌ Wrong - names don't match
NavigationData(
  url: '/user/:userId',
  builder: (context, routeData, globalData) => UserPage(
    id: routeData.pathParameters['id'], // ❌ Returns null
  ),
)

Combining with query parameters

Path parameters and query parameters work together seamlessly:
NavigationData(
  url: '/project/:projectId',
  builder: (context, routeData, globalData) => ProjectPage(
    id: int.tryParse(routeData.pathParameters['projectId'] ?? '') ?? 0,
    tab: routeData.queryParameters['tab'] ?? 'overview',
  ),
)
Navigate with both:
NavigationManager.instance.push(
  '/project/320',
  queryParameters: {'tab': 'settings'},
);
Path parameters work with deeplinks and can be remapped:
DeeplinkDestination(
  deeplinkUrl: '/link/project/:projectId',
  destinationUrl: '/project/:projectId',
  mapArgumentsFunction: (pathParameters, queryParameters) {
    // Access and transform path parameters
    String projectId = pathParameters['projectId'] ?? '';
    return {'id': projectId};
  },
)

Best practices

Use path parameters for:
  • Required route identifiers (IDs, slugs, usernames)
  • Hierarchical data (company/project, category/product)
  • SEO-friendly URLs on web
Use descriptive parameter names:
  • '/user/:userId' is better than '/user/:id'
  • '/post/:postSlug' is better than '/post/:slug'

Next steps

Query parameters

Learn about optional URL parameters

Deeplinks

Handle dynamic deeplink URLs

Build docs developers (and LLMs) love