VRouter logo

VRouter website
Join our discord
Github repo
pub package

A Flutter package that makes navigation and routing easy.

Learn more at vrouter.dev

Here are a few things that this package will make easy:

  • Automated web url handling
  • Nesting routes
  • Transition
  • Advanced url naming
  • Reacting to route changing
  • Customizable pop events
  • And much more…


Getting started


VRouter is a widget which handles the navigation, it acts as a MaterialApp but also takes a routes arguments.

 debugShowCheckedModeBanner: false, // VRouter acts as a MaterialApp
 routes: [...], // Put your VRouteElements here


VRouteElements are the building blocs of your routes.

Note that they are not widgets but the way you use them is very similar to widgets.


VWidget maps a path to a widget:

VWidget(path: '/login', widget: LoginScreen())


VGuard allows you to take actions when the route is accessed/leaved:

 beforeEnter: (vRedirector) async => , // Triggered when a route in stackedRoutes is first displayed
 beforeUpdate: (vRedirector) async => , // Triggered when a route in stackedRoutes is displayed but changes
 beforeLeave: (vRedirector, _) async => , // Triggered when VGuard is not part of a new route
 stackedRoutes: [...],


VRouteRedirector redirects from a route to another:

VRouteRedirector(path: '/old/home', redirectTo: '/home')


VNester are used when you need to nested widgets instead of stacking them:

 path: '/home',
 widgetBuilder: (child) => MyScaffold(body: child), // child will take the value of the widget in nestedRoutes
 nestedRoutes: [
   VWidget(path: 'profile', widget: ProfileScreen()), // path '/home/profile'
   VWidget(path: 'settings', widget: SettingsScreen()), // path '/home/settings'


VPopHandler helps you control pop events:

 onPop: (vRedirector) async =>, // Called when this VRouteElement is popped
 onSystemPop: (vRedirector) async =>, // Called when this VRouteElement is popped by android back button
 stackedRoutes: [...],


Navigating is easy, just access VRouter with context.vRouter and navigate:

context.vRouter.to('/home'); // Push the url '/home'

context.vRouter.toSegments(['home', 'settings']); // Push the url '/home/settings'

Useful notions


VRouteElements are designed like widgets: compose them to create the route you need.

Compose VRouteElements

To compose VRouteElements, use the stackedRoutes attribute (or the nestedRoutes attribute for VNester):

// Composing a VGuard and a VWidget
 beforeEnter: (vRedirector) async => !isLoggedIn ? vRedirector.to('/login') : null,
 stackedRoutes: [
   VWidget(path: '/home', widget: HomeScreen()),

Create custom VRouteElements

You can even create your own VRouteElement, as you extend VWidget. You just need to extends VRouteElementBuilder:

class HomeRoute extends VRouteElementBuilder {
 static String home = '/home';

 List<VRouteElement> buildRoutes() {
   return [
       // LoginRoute.login = '/login' for example
       beforeEnter: (vRedirector) async => !isLoggedIn ? vRedirector.to(LoginRoute.login) : null,
       stackedRoutes: [
         VWidget(path: home, widget: HomeScreen()),

and then use this VRouteElement as any other:

 routes: [

This can be used to:

  • Separate you different routes in different VRouteElement
  • Create reusable VRouteElement
  • Use static String to organise your paths

Note: you often want to use a shared VNester in different VRouteElementBuilders, for this specific use case, see vrouter.dev/Custom VRouteElement And Scaling


Relative path

Paths can be relative: if you don’t start your path with /. If you use null, the path will be the one of the parent:

 path: '/home',
 widgetBuilder: (child) => MyScaffold(body: child),
 nestedRoutes: [
   VWidget(path: null, widget: HomeScreen()), // Matches '/home'
   VWidget(path: 'settings', widget: SettingsScreen()), // Matches '/home/settings'

Path parameters

Paths can have path parameters, just use “:” in front of the path parameter’s name:

VWidget(path: '/user/:id', widget: UserScreen())

And access it in your widgets using:



Wildcards are noted * and there are of one of two types:

  • Trailing wildcards (a path ending with *) will match everything
  • an in-path wildcard (a wildcard between slashes) will match one word

// Redirects any path to '/unknown'
VRouteRedirector(path: '*', redirectTo: '/unknown')

Path parameters regexp

Path parameters can use regex, just put the regex in parentheses. This is often used in VRedirector to redirect any unknown route:

// The path parameter name is “bookId” and it uses the regex “\d+” to match only digits
VWidget(path: r':bookId(\d+)', widget: BookScreen())

Note that such a VRedirector should be used as your last route otherwise it will always be matched.


Use aliases for multiple paths:

// Matches '/settings' and '/other'
VWidget(path: '/settings', aliases: ['/other'], widget: SettingsScreen())


You often want to redirect or stop the current redirection in VGuard and VPopHandler. For that purpose, you get a VRedirector:

 beforeEnter: (vRedirector) async => !isLoggedIn ? vRedirector.to('/login') : null,
 stackedRoutes: [...],


VRouter contains data (such as the path or path parameters) that you might want to access in your widget tree. There are 2 ways of doing do:

// Use the context

// Use the builder constructor of some VWidget or VNester
  path: '/:id', 
  builder: (context, state) => Book(id: state.pathParameters['id'] as int),

Go Beyond

We have just scratched the surface of what VRouter can do. Here are a few other things which you might like.

Initial url

Maybe you want to redirect people to a certain part of your app when they first launch it, then use initialUrl:

 initialUrl: '/home',
 routes: [...],


By default, VRouter shows logs of every navigation events.

You can remove these logs using VLogs.none:

  logs: VLogs.none,
  routes: [...],

Named route

You might have to access a deeply nested VRouteElement and don’t want to have to write the full url. Just give a name to this VRouteElement:

VWidget(path: 'something', widget: SomeWidget(), name: 'deep')

And navigate using toNamed:



You can either specify a default transition in VRouter, or a custom one in VWidget or VNester

 // Default transition
 buildTransition: (animation1, _, child) => FadeTransition(opacity: animation1, child: child),
 routes: [
   // The custom ScaleTransition will play for '/user'
     path: '/user',
     widget: UsersScreen(),
     buildTransition: (animation1, _, child) => ScaleTransition(scale: animation1, child: child),
   // The default FadeTransition will play for '/settings'
   VWidget(path: '/settings', widget: SettingsScreen()),

Much more

There is so much more that this package can do, check out the example
or have a look at the vrouter.dev website for more documentation and more examples.

Also don’t hesitate to join us on discord !