search ESC

Searching…

No results for "".

Type at least 2 characters to search.

Docs
You are viewing an older version (v1.0.0-alpha.1). Go to the latest.

Routing

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 :param syntax (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');

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:list after making routing changes to verify your routes are registered correctly.