search ESC

Searching…

No results for "".

Type at least 2 characters to search.

Docs
You are viewing an older version (1.0.0-alpha.2). 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.

Query Parameters

Query parameters are the key-value pairs that appear after the ? in a URL (e.g., /search?q=flutter&page=2). Magic provides the Request facade to read them from the current route.

Reading Query Parameters

Use Request.query() to retrieve a single query parameter by key. It returns null when the key is absent:

// URL: /search?q=flutter&page=2
final term = Request.query('q');    // 'flutter'
final page = Request.query('page'); // '2'
final sort = Request.query('sort'); // null

Use Request.queryParams to retrieve all query parameters as a Map:

// URL: /search?q=flutter&sort=desc
final params = Request.queryParams;
// {'q': 'flutter', 'sort': 'desc'}

Pass a query map to MagicRoute.to() or MagicRoute.toNamed() to append query parameters to the URL:

// By path
MagicRoute.to('/search', query: {'q': 'flutter'});

// By name
MagicRoute.toNamed('search', query: {'q': 'flutter', 'page': '2'});

[!NOTE] Query parameters are always String values. Convert to other types after reading (e.g., int.tryParse(Request.query('page') ?? '')).

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.