Routing
- Introduction
- Basic Routing
- Route Parameters
- Named Routes
- Route Groups
- Context-Free Navigation
- Route Middleware
- Inspecting Routes
Introduction
The most basic Magic routes accept a URI and a closure, providing a very simple and expressive method of defining routes and behavior without complicated routing configuration files.
All routes for your application are defined in the lib/routes directory. These files are loaded by the RouteServiceProvider, which is included in your config/app.dart by default.
Basic Routing
The most basic route definitions involve calling a method on the MagicRoute facade:
MagicRoute.page('/', () => HomePage());
Route Methods
Use the page method to define full-screen page routes:
// Simple page
MagicRoute.page('/greeting', () => Text('Hello World'));
// Controller action
MagicRoute.page('/dashboard', () => DashboardController.instance.index());
// Inline widget
MagicRoute.page('/about', () => AboutPage());
The Initial Route
Configure your application's initial route via MagicApplication:
runApp(
MagicApplication(
initialRoute: '/dashboard',
// ...
),
);
Route Parameters
Required Parameters
Sometimes you need to capture segments of the URI. For example, to capture a user's ID:
MagicRoute.page('/user/:id', (id) {
return UserProfileView(userId: id);
});
You may define as many route parameters as required:
MagicRoute.page('/posts/:postId/comments/:commentId', (postId, commentId) {
return CommentView(postId: postId, commentId: commentId);
});
[!NOTE] Magic uses
:paramsyntax (like Express.js) instead of Laravel's{param}syntax.
Named Routes
Named routes allow convenient generation of URLs or redirects for specific routes. Specify a name by chaining the name method:
MagicRoute.page('/user/profile', () => ProfileView())
.name('profile');
MagicRoute.page('/user/:id', (id) => UserView(id: id))
.name('user.show');
Navigating To Named Routes
Once you have assigned a name to a route, you may use it when navigating:
// Navigate to named route
MagicRoute.toNamed('profile');
// With path parameters
MagicRoute.toNamed('user.show', params: {'id': '42'});
// With query parameters
MagicRoute.toNamed('search', query: {'q': 'flutter'});
Route Groups
Route groups allow you to share route attributes, such as middleware or prefixes, across multiple routes.
Middleware
Assign middleware to all routes within a group:
MagicRoute.group(
middleware: ['auth'],
routes: () {
MagicRoute.page('/dashboard', () => DashboardView());
MagicRoute.page('/profile', () => ProfileView());
},
);
Prefixes
Add a path prefix to all routes in a group:
MagicRoute.group(
prefix: '/admin',
middleware: ['auth', 'admin'],
routes: () {
MagicRoute.page('/', () => AdminDashboard()); // /admin
MagicRoute.page('/users', () => AdminUsers()); // /admin/users
MagicRoute.page('/settings', () => AdminSettings()); // /admin/settings
},
);
Nested Groups
Groups can be nested. Child groups inherit parent attributes:
MagicRoute.group(
prefix: '/admin',
middleware: ['auth'],
routes: () {
MagicRoute.group(
prefix: '/users',
routes: () {
MagicRoute.page('/', () => UserList()); // /admin/users
MagicRoute.page('/:id', (id) => UserShow(id: id)); // /admin/users/:id
},
);
},
);
Layouts (Shell Routes)
Assign a persistent layout to all routes within a group. The layout persists while child pages change—perfect for tab bars, navigation rails, and sidebars:
MagicRoute.group(
layout: (child) => AppLayout(child: child),
middleware: ['auth'],
routes: () {
MagicRoute.page('/', () => DashboardView());
MagicRoute.page('/monitors', () => MonitorsView());
MagicRoute.page('/settings', () => SettingsView());
},
);
Your layout widget should accept and render the child parameter:
class AppLayout extends StatelessWidget {
final Widget child;
const AppLayout({required this.child, super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
AppSidebar(),
Expanded(child: child), // Child pages render here
],
),
);
}
}
[!TIP] Use layouts for any UI that should persist across page navigation, such as sidebars, bottom navigation bars, or headers.
Context-Free Navigation
You may navigate from anywhere in your application—controllers, services, or pure Dart classes—without needing BuildContext:
// Replace current route
MagicRoute.to('/dashboard');
// Push onto navigation stack (back button works)
MagicRoute.push('/details');
// Go back
MagicRoute.back();
// Replace current route (no back)
MagicRoute.replace('/home');
// With query parameters
MagicRoute.to('/search', query: {'q': 'flutter'});
From Controllers
class AuthController extends MagicController {
Future logout() async {
await Auth.logout();
MagicRoute.to('/login'); // No context needed!
}
}
Route Middleware
Assign middleware to individual routes using the middleware method:
MagicRoute.page('/profile', () => ProfileView())
.middleware(['auth']);
MagicRoute.page('/admin', () => AdminPanel())
.middleware(['auth', 'admin']);
See the Middleware documentation for details on creating custom middleware.
Inspecting Routes
Magic CLI provides a command to list all registered routes:
magic route:list
Output:
+---------------------+------------+----------+
| URI | Middleware | File |
+---------------------+------------+----------+
| / | auth | app.dart |
| /monitors | auth | app.dart |
| /auth/login | - | auth.dart|
| /settings/team | auth | app.dart |
+---------------------+------------+----------+
[!TIP] Run
magic route:listafter making routing changes to verify your routes are registered correctly.